Comparar commits
389 Commits
| Autor | SHA1 | Data | |
|---|---|---|---|
| d36934e35b | |||
| 41f7258e25 | |||
| 24cb4fbb36 | |||
| 6fa022013d | |||
| 9274846970 | |||
| 9050f8da53 | |||
| f7039848c2 | |||
| d4e27d8bbb | |||
| 13f5ebe9d7 | |||
| db787c7cab | |||
| 21a04beb25 | |||
| 1565e979ec | |||
| 927395630b | |||
| 79d164b2d4 | |||
| 3cfbc20d75 | |||
| fcaa7d71b7 | |||
| 423516ddd9 | |||
| afc3088427 | |||
| f1ac6c05b1 | |||
| d965f1b7e5 | |||
| 172722c63b | |||
| 702564f5c6 | |||
| 329d85a9aa | |||
| 0a31584760 | |||
| 01f1568dda | |||
| 883b0c2efc | |||
| 57c91c35b0 | |||
| 00a2a2a107 | |||
| eb164c8a0e | |||
| 61c4ffd982 | |||
| 6df992580a | |||
| f4af9ec780 | |||
| 81dc71a2de | |||
| 9f36330fe6 | |||
| f73094179e | |||
| a430281b46 | |||
| 57820afdfa | |||
| 8ed7d6e4d5 | |||
| c23701d7f1 | |||
| 8964454bf4 | |||
| 153370bbcb | |||
| ff28da5bde | |||
| 2ea7a8724a | |||
| d073dda5c4 | |||
| 93be1b8d63 | |||
| 0991624234 | |||
| bc28971f10 | |||
| c98b6ba8a8 | |||
| 600eed61fc | |||
| 0088c2e6ca | |||
| cdf7628287 | |||
| 7b7e9eec24 | |||
| 326ba11c87 | |||
| eee41a6aee | |||
| 20fa95def8 | |||
| 43c3b11253 | |||
| 7c708c1072 | |||
| 33b287000c | |||
| 97ed92d785 | |||
| 218a028dd6 | |||
| a53977e49c | |||
| 3c718af85e | |||
| ae3c113ca0 | |||
| b3f3135157 | |||
| 3d8e1d39d7 | |||
| a7335a5caa | |||
| a2f88584bf | |||
| 61c3de631e | |||
| 2aa72aa0c4 | |||
| c2c7e4dac3 | |||
| 626eed9ecb | |||
| e1420f661d | |||
| e8faa99a46 | |||
| 8f2fe55f54 | |||
| e587cf0bee | |||
| 1fb0ed5c5a | |||
| 5163caed25 | |||
| bd90859055 | |||
| 50cdb76249 | |||
| 36e7e2c4f4 | |||
| c66ca18e1f | |||
| f68a82eca4 | |||
| 24f74a733b | |||
| ae545a9ee9 | |||
| 45a4f2f28f | |||
| b533629835 | |||
| 282442c00c | |||
| 1a014c9b36 | |||
| 66385ed4bd | |||
| f6013a8683 | |||
| 3b798fb593 | |||
| ab36feefbe | |||
| a509e6af7e | |||
| 607989c28e | |||
| a31c855141 | |||
| 6a288763b1 | |||
| 16d960d5b7 | |||
| ed659ea663 | |||
| 71af3e1548 | |||
| 999f90cc00 | |||
| 51214f9dfd | |||
| aaa871157b | |||
| 488d74c950 | |||
| bf23980e57 | |||
| b175265385 | |||
| 8403514604 | |||
| 9e1a2c674e | |||
| cbe36a41c3 | |||
| 14e7397a53 | |||
| abbda6cd88 | |||
| 44411a45c7 | |||
| 79b2b36ecf | |||
| 7a05419def | |||
| 8423d2ab6c | |||
| 44ab7dd0cd | |||
| e6f4851d00 | |||
| e3d89e9fae | |||
| 4623962a11 | |||
| 3c37901918 | |||
| b763469114 | |||
| 33e68d7534 | |||
| 903d0b3260 | |||
| 52ca544328 | |||
| 80ada66e91 | |||
| 4e9bc9c955 | |||
| d406cd3fd1 | |||
| 0e375ca376 | |||
| 6c4d758cd9 | |||
| 1cd7c82c03 | |||
| 641dd33d74 | |||
| e48e4dea7b | |||
| 0aa163d84d | |||
| c2a233db51 | |||
| e90d3008c2 | |||
| 59879ba38f | |||
| 266e280860 | |||
| 6c489217f5 | |||
| d4bae3c377 | |||
| 15cf1bffc8 | |||
| bb7d6f4cdc | |||
| 1936cbb781 | |||
| 55f4ad68a9 | |||
| a45f936366 | |||
| dcef0205b2 | |||
| 61976be9c1 | |||
| fd5f33570d | |||
| ff568d0be2 | |||
| b29d71b2cc | |||
| 2ee21bf4d2 | |||
| cfd5178d54 | |||
| c1b6e5a8b3 | |||
| 02b32238d8 | |||
| 5f47023759 | |||
| 68e3966257 | |||
| 024fed3dc5 | |||
| b2e5a290ce | |||
| 8f9fc7a7b1 | |||
| 2bbd2a5cd8 | |||
| 18b476bc33 | |||
| 06aeff27df | |||
| 683a1ea99b | |||
| dac08fa727 | |||
| 22cffd2ac6 | |||
| b2e00b042c | |||
| d2240c7228 | |||
| 68d00d0d00 | |||
| b37e864cee | |||
| acb58d0ac0 | |||
| 2edfafb988 | |||
| 5ea46a44be | |||
| a6ec820c1e | |||
| 228b641b8d | |||
| 22bf8b290a | |||
| c851427848 | |||
| 8d6d61b3aa | |||
| 0787250aca | |||
| 2c2492b6ea | |||
| 2d8ce2173a | |||
| 35faa0c2ec | |||
| 7c5ea7cba4 | |||
| cace9448df | |||
| 03a13199b4 | |||
| 177d16015b | |||
| 54080f7119 | |||
| 66a0b0275d | |||
| 7602f3ccfb | |||
| dcadf6668a | |||
| 379fee78f4 | |||
| d4f3d0c37b | |||
| 0954fef276 | |||
| eb947ef52c | |||
| 0c08bf6ce0 | |||
| a9db2813a8 | |||
| 34682fc8a3 | |||
| 9b0b68c75e | |||
| f9854aead9 | |||
| 468abb641b | |||
| c62774c572 | |||
| 68f2824fd0 | |||
| 556dace457 | |||
| 6f80977b35 | |||
| ef8c076a99 | |||
| 31ea98a59d | |||
| 6c05436a0e | |||
| 859f37b96c | |||
| 05874bb9c4 | |||
| ca7353e4d4 | |||
| b9d48c9580 | |||
| f7edd64f9f | |||
| 6fafc0ab29 | |||
| 93a45c4ed1 | |||
| 05263813f8 | |||
| bb3098e0bc | |||
| 1288038edf | |||
| 33e0083cda | |||
| 7a01dbb08f | |||
| 2cd3bc330c | |||
| eeeadb8221 | |||
| 290c75669e | |||
| 0a01ba47e5 | |||
| 9b30d16a16 | |||
| 01250390de | |||
| c22b1bef74 | |||
| 4820fbc5ee | |||
| 6285f1d1fd | |||
| 1f77742a18 | |||
| f1a83c18db | |||
| 8a2956334e | |||
| ceacaa27b1 | |||
| 8032c7bfec | |||
| b3c83ce5e1 | |||
| f278248402 | |||
| 99f2950cb2 | |||
| 712acb9639 | |||
| 4b0189b1b6 | |||
| eda8800636 | |||
| 9b95c2f506 | |||
| 6241ec2f5b | |||
| 3d9f8bd740 | |||
| 3c6f6832a9 | |||
| e0e009368d | |||
| df29292d8f | |||
| 461b41b7e4 | |||
| f02b197322 | |||
| 2ef092a720 | |||
| e305fd4823 | |||
| d1e4518961 | |||
| 3948590ded | |||
| 7b2b9630c7 | |||
| a1b0ffc1a7 | |||
| fbbe908f35 | |||
| cdf3215ab3 | |||
| faaf5737f2 | |||
| e843d53ec0 | |||
| cbb080f327 | |||
| 4621d77a17 | |||
| 77935a845c | |||
| 62c7c113ca | |||
| 107aaf0c93 | |||
| b90263b481 | |||
| d2f6a86e54 | |||
| e0c294238f | |||
| 487327256a | |||
| e4e0dff046 | |||
| 8e5881d7e4 | |||
| 780fe88fe7 | |||
| 9f73f59d2e | |||
| 6e7cf77e3d | |||
| 6207419948 | |||
| 112cf55ea9 | |||
| b7587a1f1e | |||
| 01a430b052 | |||
| bdc12d0bd9 | |||
| 9b5b035e5e | |||
| d6f98f2db2 | |||
| c0013a4329 | |||
| 46774fb1eb | |||
| 327b38094a | |||
| ffe50c6cb0 | |||
| 6b7d61d7db | |||
| 6d8b5408bf | |||
| 408bf5ef12 | |||
| 0610c2193e | |||
| 866a1b4137 | |||
| 19c9c80c7d | |||
| 9aba19a738 | |||
| 616d2305cd | |||
| 7be326ba1b | |||
| f6ec4f3700 | |||
| ece2d4af18 | |||
| c22499bcc6 | |||
| c12198ac25 | |||
| 5a6ebd4156 | |||
| 0fdec5246b | |||
| bbed68e588 | |||
| 7ce7123dd9 | |||
| 3ceff11112 | |||
| 90c663c6cd | |||
| 51314b765d | |||
| 445cc29ef0 | |||
| ae5aba694f | |||
| 8e570b8fc2 | |||
| 14482d2cf5 | |||
| 49d5a81d02 | |||
| 3e980a84fd | |||
| af14f82a7f | |||
| ff4fa3581e | |||
| 2bef59e79b | |||
| 2e5b72cfef | |||
| 3aa638ea42 | |||
| edf2f897d1 | |||
| cacffadc61 | |||
| 951f8f4e4b | |||
| f459c91631 | |||
| b1bf1ccbdc | |||
| 47b75368af | |||
| aa7b1f5c76 | |||
| ecb365f275 | |||
| 4be5999770 | |||
| f4e1a7b1f7 | |||
| c771da74d4 | |||
| 914417ee3e | |||
| a4f3656093 | |||
| 763b9e4827 | |||
| 1231bf6ce0 | |||
| f76510ce26 | |||
| fa4eafa8e3 | |||
| 685991cd98 | |||
| 67771564be | |||
| 5b67ca1a00 | |||
| 5d3d436105 | |||
| ead90e6862 | |||
| 92937617f5 | |||
| 0871cd7492 | |||
| 42b6f365c5 | |||
| d1f9e80b5d | |||
| 175a3243a5 | |||
| 4c42b16059 | |||
| 5a4548d426 | |||
| bb587d17d9 | |||
| c004124f85 | |||
| 63e81e33d3 | |||
| 8b8c385441 | |||
| cad96e24dd | |||
| c688fc67ef | |||
| 68c34fad86 | |||
| 96af51afce | |||
| f20fb1aec5 | |||
| f6d7b77e8e | |||
| ce6f78dd89 | |||
| 61ace00ab3 | |||
| 19800c66b8 | |||
| 69f931b298 | |||
| 2a15672b98 | |||
| 7e788e64b2 | |||
| 7e59e3b23f | |||
| 5f361846bb | |||
| c1c676d892 | |||
| 7240dc6ffe | |||
| 848a08f4b7 | |||
| 93c2ceae8c | |||
| 778c26e90c | |||
| 9e1d180e81 | |||
| 4c671418ee | |||
| 95bd012bbe | |||
| d721ae7daf | |||
| 21494ffc6c | |||
| e1a11d0741 | |||
| 64faaca516 | |||
| 54f9620fc5 | |||
| 5c327b7100 | |||
| e9e0457238 | |||
| febbdcaf27 | |||
| b0e962d37f | |||
| d0af23d7c8 | |||
| 548d58cf8e | |||
| 35beec7104 | |||
| 63c6a6e4f4 | |||
| 4a2f76503c | |||
| 0d65ef9ccc | |||
| 881db618ca | |||
| 1f64315092 | |||
| 2cb777bfdc | |||
| dd3be7d4c9 | |||
| 17fe09fff9 | |||
| 47a996cf15 | |||
| 1de9da4558 | |||
| 85fe231dcc | |||
| 06e9f576b2 |
@@ -18,3 +18,8 @@ build/*
|
||||
NUL
|
||||
CuraEngine.layout
|
||||
CuraEngine.cbp
|
||||
documentation/html/*
|
||||
documentation/latex/*
|
||||
|
||||
*kdev*
|
||||
*.kate-swp
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
project(CuraEngine)
|
||||
|
||||
cmake_minimum_required(VERSION 2.8.12)
|
||||
|
||||
find_package(Arcus REQUIRED)
|
||||
|
||||
if(NOT ${CMAKE_VERSION} VERSION_LESS 3.1)
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS "-std=c++11")
|
||||
endif()
|
||||
|
||||
# Add warnings
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
|
||||
|
||||
if(NOT APPLE AND NOT WIN32)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static-libstdc++")
|
||||
elseif(APPLE)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
|
||||
endif()
|
||||
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR} libs)
|
||||
|
||||
add_library(clipper STATIC libs/clipper/clipper.cpp)
|
||||
|
||||
set(engine_SRCS
|
||||
src/bridge.cpp
|
||||
src/comb.cpp
|
||||
src/commandSocket.cpp
|
||||
src/gcodeExport.cpp
|
||||
src/gcodePlanner.cpp
|
||||
src/infill.cpp
|
||||
src/inset.cpp
|
||||
src/layerPart.cpp
|
||||
src/main.cpp
|
||||
src/mesh.cpp
|
||||
src/multiVolumes.cpp
|
||||
src/pathOrderOptimizer.cpp
|
||||
src/polygonOptimizer.cpp
|
||||
src/raft.cpp
|
||||
src/settingRegistry.cpp
|
||||
src/settings.cpp
|
||||
src/skin.cpp
|
||||
src/skirt.cpp
|
||||
src/slicer.cpp
|
||||
src/support.cpp
|
||||
src/timeEstimate.cpp
|
||||
src/Weaver.cpp
|
||||
src/Wireframe2gcode.cpp
|
||||
|
||||
src/modelFile/modelFile.cpp
|
||||
|
||||
src/utils/gettime.cpp
|
||||
src/utils/logoutput.cpp
|
||||
src/utils/polygonUtils.cpp
|
||||
)
|
||||
|
||||
protobuf_generate_cpp(engine_PB_SRCS engine_PB_HEADERS Cura.proto)
|
||||
|
||||
add_executable(CuraEngine ${engine_SRCS} ${engine_PB_SRCS})
|
||||
target_link_libraries(CuraEngine clipper Arcus)
|
||||
|
||||
add_executable(Test src/test.cpp)
|
||||
target_link_libraries(Test clipper)
|
||||
|
||||
if (UNIX)
|
||||
target_link_libraries(CuraEngine pthread)
|
||||
endif()
|
||||
|
||||
include(GNUInstallDirs)
|
||||
install(TARGETS CuraEngine DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
@@ -0,0 +1,86 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package Cura;
|
||||
|
||||
// typeid 1
|
||||
message ObjectList {
|
||||
repeated Object objects = 1;
|
||||
}
|
||||
|
||||
message Object {
|
||||
int64 id = 1;
|
||||
bytes vertices = 2; //An array of 3 floats.
|
||||
bytes normals = 3; //An array of 3 floats.
|
||||
bytes indices = 4; //An array of ints.
|
||||
repeated Setting settings = 5; // Setting override per object, overruling the global settings.
|
||||
}
|
||||
|
||||
// typeid 3
|
||||
message Progress {
|
||||
float amount = 1;
|
||||
}
|
||||
|
||||
// typeid 2
|
||||
message SlicedObjectList {
|
||||
repeated SlicedObject objects = 1;
|
||||
}
|
||||
|
||||
message SlicedObject {
|
||||
int64 id = 1;
|
||||
|
||||
repeated Layer layers = 2;
|
||||
}
|
||||
|
||||
message Layer {
|
||||
int32 id = 1;
|
||||
|
||||
float height = 2;
|
||||
float thickness = 3;
|
||||
|
||||
repeated Polygon polygons = 4;
|
||||
}
|
||||
|
||||
message Polygon {
|
||||
enum Type {
|
||||
NoneType = 0;
|
||||
Inset0Type = 1;
|
||||
InsetXType = 2;
|
||||
SkinType = 3;
|
||||
SupportType = 4;
|
||||
SkirtType = 5;
|
||||
InfillType = 6;
|
||||
SupportInfillType = 7;
|
||||
}
|
||||
Type type = 1;
|
||||
bytes points = 2;
|
||||
float line_width = 3;
|
||||
}
|
||||
|
||||
// typeid 4
|
||||
message GCodeLayer {
|
||||
int64 id = 1;
|
||||
bytes data = 2;
|
||||
}
|
||||
|
||||
// typeid 5
|
||||
message ObjectPrintTime {
|
||||
int64 id = 1;
|
||||
float time = 2;
|
||||
float material_amount = 3;
|
||||
}
|
||||
|
||||
// typeid 6
|
||||
message SettingList {
|
||||
repeated Setting settings = 1;
|
||||
}
|
||||
|
||||
message Setting {
|
||||
string name = 1;
|
||||
|
||||
bytes value = 2;
|
||||
}
|
||||
|
||||
// typeid 7
|
||||
message GCodePrefix {
|
||||
bytes data = 2;
|
||||
}
|
||||
+2303
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
-89
@@ -1,89 +0,0 @@
|
||||
#
|
||||
# Makefile for CuraEngine
|
||||
#
|
||||
|
||||
BUILD_DIR = build
|
||||
SRC_DIR = src
|
||||
LIBS_DIR = libs
|
||||
|
||||
BUILD_TYPE = RELEASE
|
||||
|
||||
VERSION ?= DEV
|
||||
CXX ?= g++
|
||||
CFLAGS += -c -Wall -Wextra -Wold-style-cast -Woverloaded-virtual -std=c++11 -DVERSION=\"$(VERSION)\" -isystem libs
|
||||
|
||||
ifeq ($(BUILD_TYPE),DEBUG)
|
||||
CFLAGS+=-ggdb -Og -g
|
||||
endif
|
||||
ifeq ($(BUILD_TYPE),PROFILE)
|
||||
CFLAGS+= -pg
|
||||
endif
|
||||
ifeq ($(BUILD_TYPE),RELEASE)
|
||||
CFLAGS+= -O3 -fomit-frame-pointer
|
||||
endif
|
||||
|
||||
LDFLAGS += -Lbuild/ -lclipper
|
||||
|
||||
SOURCES_RAW = bridge.cpp comb.cpp gcodeExport.cpp infill.cpp inset.cpp layerPart.cpp main.cpp optimizedModel.cpp pathOrderOptimizer.cpp polygonOptimizer.cpp raft.cpp settings.cpp skin.cpp skirt.cpp slicer.cpp support.cpp timeEstimate.cpp
|
||||
SOURCES_RAW += modelFile/modelFile.cpp utils/gettime.cpp utils/logoutput.cpp utils/socket.cpp
|
||||
SOURCES = $(addprefix $(SRC_DIR)/,$(SOURCES_RAW))
|
||||
|
||||
OBJECTS_RAW = $(SOURCES_RAW:.cpp=.o)
|
||||
OBJECTS = $(addprefix $(BUILD_DIR)/,$(OBJECTS_RAW))
|
||||
|
||||
DIRS = $(sort $(dir $(OBJECTS)))
|
||||
|
||||
EXECUTABLE = $(BUILD_DIR)/CuraEngine
|
||||
|
||||
ifeq ($(OS),Windows_NT)
|
||||
#For windows make it large address aware, which allows the process to use more then 2GB of memory.
|
||||
EXECUTABLE := $(EXECUTABLE).exe
|
||||
CFLAGS += -march=pentium4 -flto
|
||||
LDFLAGS += -Wl,--large-address-aware -lm -lwsock32 -flto
|
||||
MKDIR_PREFIX = mkdir -p
|
||||
else
|
||||
MKDIR_PREFIX = mkdir -p
|
||||
UNAME := $(shell uname)
|
||||
ifeq ($(UNAME), Linux)
|
||||
OPEN_HTML=firefox
|
||||
ifeq ($(BUILD_TYPE),DEBUG)
|
||||
LDFLAGS += --static
|
||||
else
|
||||
CFLAGS += -flto
|
||||
LDFLAGS += --static -flto
|
||||
endif
|
||||
endif
|
||||
ifeq ($(UNAME), OpenBSD)
|
||||
LDFLAGS += -lm -lpthread
|
||||
endif
|
||||
ifeq ($(UNAME), Darwin)
|
||||
OPEN_HTML=open
|
||||
#For MacOS force to build
|
||||
CFLAGS += -force_cpusubtype_ALL -mmacosx-version-min=10.6 -arch x86_64 -arch i386
|
||||
LDFLAGS += -force_cpusubtype_ALL -mmacosx-version-min=10.6 -arch x86_64 -arch i386
|
||||
endif
|
||||
endif
|
||||
|
||||
all: $(DIRS) $(SOURCES) $(EXECUTABLE)
|
||||
|
||||
$(BUILD_DIR)/libclipper.a: $(LIBS_DIR)/clipper/clipper.cpp
|
||||
$(CXX) $(CFLAGS) -o $(BUILD_DIR)/libclipper.a $(LIBS_DIR)/clipper/clipper.cpp
|
||||
|
||||
$(EXECUTABLE): $(OBJECTS) $(BUILD_DIR)/libclipper.a
|
||||
$(CXX) $(OBJECTS) -o $@ $(LDFLAGS)
|
||||
|
||||
$(DIRS):
|
||||
-@$(MKDIR_PREFIX) $(DIRS)
|
||||
|
||||
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.cpp
|
||||
$(CXX) $(CFLAGS) $< -o $@
|
||||
|
||||
test: $(EXECUTABLE)
|
||||
python tests/runtest.py $(abspath $(EXECUTABLE))
|
||||
|
||||
## clean stuff
|
||||
clean:
|
||||
rm -f $(EXECUTABLE) $(OBJECTS) $(BUILD_DIR)/libclipper.a
|
||||
|
||||
help:
|
||||
@cat Makefile |grep \#\#| grep \: |cut -d\# -f3
|
||||
+41
@@ -16,6 +16,47 @@ Terms of the license can be found in the LICENSE file. Or at http://www.gnu.org/
|
||||
|
||||
But in general it boils down to: You need to share the source of any CuraEngine modifications if you make an application with the CuraEngine. (Even if you make a web-based slicer, you still need to share the source!)
|
||||
|
||||
How to Install
|
||||
==============
|
||||
1. Clone the repository from https://github.com/Ultimaker/CuraEngine.git (the URL at the right hand side of this page).
|
||||
2. Install Protobuf (see below)
|
||||
3. Install libArcus (see https://github.com/Ultimaker/libArcus)
|
||||
|
||||
In order to compile CuraEngine, either use CMake or start a project in your preferred IDE.
|
||||
CMake compilation:
|
||||
1. Navigate to the CuraEngine directory and execute the following commands
|
||||
2. $ mkdir build && cd build
|
||||
3. $ cmake ..
|
||||
4. $ make
|
||||
|
||||
Project files generation:
|
||||
1. Navigate to the CuraEngine directory and execute the following commands
|
||||
2. cmake . -G "CodeBlocks - Unix Makefiles"
|
||||
3. (for a list of supported IDE's see http://www.cmake.org/Wiki/CMake_Generator_Specific_Information#Code::Blocks_Generator)
|
||||
|
||||
Installing Protobuf
|
||||
-------------------
|
||||
1. Be sure to have libtool installed.
|
||||
2. Download protobuf from https://github.com/google/protobuf/ (download ZIP and unZIP at desired location, or clone the repo) The protocol buffer is used for communication between the CuraEngine and the GUI.
|
||||
3. Before installing protobuf, change autogen.sh : comment line 18 to line 38 using '#'s. This removes the dependency on gtest-1.7.0.
|
||||
4. Run autogen.sh from the protobuf directory:
|
||||
$ ./autogen.sh
|
||||
5. $ ./configure
|
||||
6. $ make
|
||||
7. $ make install # Requires superused priviliges.
|
||||
8. (In case the shared library cannot be loaded, you can try "sudo ldconfig" on Linux systems)
|
||||
|
||||
Running
|
||||
=======
|
||||
Other than running CuraEngine from a frontend, such as PluggableCura, one can run CuraEngine from the command line.
|
||||
An example run for an UM2 machine looks as follows:
|
||||
* Navigate to the CuraEngine directory and execute the following
|
||||
```
|
||||
./build/CuraEngine -v -j fdmprinter.json -s machine_start_gcode=";FLAVOR:UltiGCode
|
||||
;TIME:10000000
|
||||
;MATERIAL:2000
|
||||
;MATERIAL2:0" -s mesh_position_x=115.0 -s mesh_position_y=112.5 -s mesh_position_z=0 -s material_diameter=1.128 -o "output/test.gcode" "/path/to/model.stl"
|
||||
```
|
||||
|
||||
Internals
|
||||
=========
|
||||
|
||||
Link simbólico
+1
@@ -0,0 +1 @@
|
||||
html/index.html
|
||||
Arquivo binário não exibido.
|
Depois Largura: | Altura: | Tamanho: 18 KiB |
@@ -0,0 +1,126 @@
|
||||
Code Conventions
|
||||
=======
|
||||
Note that the code convention described here have not all yet been fully implemented.
|
||||
|
||||
Bracketing and indenting
|
||||
-----
|
||||
~~~~~~~~~~~~~~~{.cpp}
|
||||
if (condition) // brackets always on new lines
|
||||
{ // allways a bracket after an if, for, while, etc.
|
||||
// indent always with 4 spaces, never with tabs
|
||||
}
|
||||
else // else on new line
|
||||
{
|
||||
// more code
|
||||
}
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Naming conventions
|
||||
------
|
||||
* variables: lower_case_with_underscores
|
||||
* functions: loweCamelCase
|
||||
* classes: UpperCamelCase
|
||||
* macros: UPPER_CASE_WITH_UNDERSCORES
|
||||
~~~~~~~~~~~~~~~{.cpp}
|
||||
#define UPPER_CASE_MACRO 1
|
||||
|
||||
class UpperCamelCase
|
||||
{
|
||||
private:
|
||||
MemberVariableObject with_underscores;
|
||||
public:
|
||||
MemberVariableObject with_underscores;
|
||||
|
||||
public:
|
||||
UpperCamelCase();
|
||||
~UpperCamelCase();
|
||||
|
||||
// start with input variable(s) and end with output variable(s)
|
||||
void lowerCamelCaseFunctions(ParamObject& also_with_underscores)
|
||||
{
|
||||
LocalObject under_scores;
|
||||
}
|
||||
private:
|
||||
void putFunctionsAndVariablesInSeperatePublicPrivateBlocks();
|
||||
};
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Ordering
|
||||
----
|
||||
~~~~~~~~~~~~~~~{.cpp}
|
||||
class Example
|
||||
{
|
||||
// start with input variable(s) and end with output parameter(s)
|
||||
void function1(ParamObject& input_variable, int setting_parameter, ParamObject2& return_parameter)
|
||||
{
|
||||
function2();
|
||||
function3();
|
||||
}
|
||||
|
||||
// place functions called solely by one other function below it chronologically
|
||||
void function2();
|
||||
|
||||
void function3();
|
||||
};
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Documentation
|
||||
----
|
||||
We use [Doxygen](www.doxygen.org/) to generate documentation. Try to keep your documentation in doxygen style.
|
||||
|
||||
Here's a small example:
|
||||
~~~~~~~~~~~~~~~{.cpp}
|
||||
/ *!
|
||||
* Doxygen style comments!
|
||||
*
|
||||
* \param param1 explanation may refer to another \p param2
|
||||
* /
|
||||
void function(int param1, int param2)
|
||||
{
|
||||
// non-doxygen style comments on implementation details
|
||||
}
|
||||
|
||||
int member; //!< inline doxygen comment on the entry to the left
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Files
|
||||
--------
|
||||
For a file Foo.h (UpperCamelCase):
|
||||
~~~~~~~~~~~~~~~{.cpp}
|
||||
#ifndef FOO_H
|
||||
#define FOO_H
|
||||
// [content]
|
||||
#endif//FOO_H
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
Other
|
||||
----
|
||||
~~~~~~~~~~~~~~~{.cpp}
|
||||
#include <all>
|
||||
#include <includes>
|
||||
#include <on>
|
||||
#include <top>
|
||||
|
||||
#include <first_system_includes>
|
||||
|
||||
#include <then_library_includes>
|
||||
|
||||
#include "finally_local_includes"
|
||||
|
||||
enum class EnumExample
|
||||
{
|
||||
ELEM0 = 0,
|
||||
ELEM1 = 1
|
||||
}
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Illegal syntax
|
||||
----
|
||||
~~~~~~~~~~~~~~~{.cpp}
|
||||
void function()
|
||||
{
|
||||
if (condition)
|
||||
single_line_outside_code_block(); // always use braces!
|
||||
}; // unneccesary semicolon after function definition is not allowed
|
||||
~~~~~~~~~~~~~~~
|
||||
@@ -0,0 +1,11 @@
|
||||
Glossary
|
||||
========
|
||||
|
||||
|
||||
Term/Synonyms | Meaning
|
||||
--- | ---
|
||||
Island/Part | isolated/unconnected part in 2D slice
|
||||
Inset | perimeter, the perimeters which are laid down around the infill
|
||||
Slicing | The act of extracting the contours of the object at a certain height (not the whole process which would also include gcode generation etc.)
|
||||
Tower (support) | A strut to reinforce parts of the support which would otherwise be unstable.
|
||||
Weaver / Neith / WirePrint | A non-layer-wise form of printing 'in thin air'
|
||||
@@ -0,0 +1,10 @@
|
||||
CuraEngine {#mainpage}
|
||||
=======
|
||||
|
||||
This is the documentation for CuraEngine, the back-end slicer of Cura.
|
||||
|
||||
[Overview](documentation/overview.md)
|
||||
|
||||
[Glossary](documentation/glossary.md)
|
||||
|
||||
[Code Conventions](documentation/code_conventions.md)
|
||||
@@ -0,0 +1,68 @@
|
||||
Overview
|
||||
========
|
||||
|
||||
Internals
|
||||
---------
|
||||
|
||||
The Cura Engine is structured as mainly .h files. This is not standard for an C++ project. However, using less cpp files makes the optimizer work harder and removes linking error issues. It's partialy a result of lazyness but also for optimalizations.
|
||||
|
||||
The .h files contain different steps called from the main.cpp file. The main.cpp file contains the global slicing logic.
|
||||
|
||||
The slicing process follows the following global steps:
|
||||
* Load 3D model
|
||||
* Analize and fix 3D model
|
||||
* Slice 3D model into 2D layers
|
||||
* Build LayerParts from sliced layers
|
||||
* Generate Insets
|
||||
* Generate up/down skins areas
|
||||
* Generate sparse infill areas
|
||||
* Generate GCode for each layer
|
||||
|
||||
Each step has more logic in it. But this is a general overview.
|
||||
All data for the engine is stored in the "SliceDataStorage". It's important to remember that only the data from the previous step is valid.
|
||||
|
||||
Coordinates are stored in 64bit integers as microns in the code. So if you see a value of 1000 then this mean 1mm of distance. This is because Clipper works on 64bit integers and microns give a high enough resolution without limiting the size too much. Note that there are some bits and pieces of code that need to be careful about 64bit overflows, especially calculating lengths sqrt(x*x+y*y) can cause overflows.
|
||||
|
||||
OptimizedModel
|
||||
--------------
|
||||
The OptimizedModel is a 3D model stored with vertex<->face relations. This gives touching face relations which are used later on to slice into layers faster.
|
||||
|
||||
Slicer
|
||||
------
|
||||
While usually the whole GCode generation process is called Slicing. The slicer in the CuraEngine is the piece of code that generates layers. Each layer has closed 2D polygons.
|
||||
These polygons are generated in a 2 step process. First all triangles are cut into lines per layer, for each layer a "line segment" is added to that layer.
|
||||
Next all these line-segments are connected to eachother to make Polygons. The vertex<->face relations of the OptimizedModel help to make this process fast, as there is a huge chance that 2 connecting faces also make 2 connecting line-segments.
|
||||
This code also fixes up small holes in the 3D model, so your model doesn't need to be perfect Manifold. It also accounts for incorrect normals, so it can flip around line-segments to fit end-to-end.
|
||||
|
||||
After the Slicer we have closed Polygons which can be used in Clipper, as Clipper can only opperate on closed 2D polygons.
|
||||
|
||||
LayerParts
|
||||
----------
|
||||
An important concept to grasp is the LayerParts. LayerParts are seperate parts inside a single layer. For example, if you have a cube. Then each layer has a single LayerPart. However, if you have a table, then the layers which build the legs have a LayerPart per leg, and thus there will be 4 LayerParts.
|
||||
A LayerPart is a seperated area inside a single layer which does not touch any other LayerParts. Most operations run on LayerParts as it reduces the amount of data to process. During GCode generation handling each LayerPart as an own step makes sure you never travel between LayerParts and thus reducing the amount of external travel.
|
||||
LayerParts are generated after the Slicer step.
|
||||
|
||||
To generate the LayerParts Clipper is used. A Clipper union with extended results gives a list of Polygons with holes in them. Each polygon is a LayerPart, and the holes are added to this LayerPart.
|
||||
|
||||
Polygons
|
||||
--------
|
||||
Holes are polygons in counter-clockwise (or at-least, in the other direction) and the polygons are guaranteed to be "even-odd", so every crossing of polygon lines will switch from "fill" to "empty".
|
||||
|
||||
Insets
|
||||
------
|
||||
Insets are also called "Perimeters" or "Loops" sometimes. Generating the insets is only a small bit of code, as Clipper does most of the heavy lifting.
|
||||
|
||||
Up/Down skin
|
||||
------------
|
||||
The skin code generates the fully filled areas, it does this with some heavy boolean Clipper action. The skin step uses data from different layers to get the job done. Check the code for details.
|
||||
The sparse infill area code is almost the same as the skin code. With the difference that it keeps the other areas and uses different offsets.
|
||||
|
||||
Note that these steps generate the areas, not the actual infill lines. The infill line paths are generated later on. So the result of this step are list of Polygons which are the areas that need to be filled.
|
||||
|
||||
GCode generation
|
||||
----------------
|
||||
The GCode generation is quite a large bit of code. As a lot is going on here. Important bits here are:
|
||||
* PathOrderOptimizer: This piece of code needs to solve a TravelingSalesmanProblem. Given a list of polygons/lines it tries to find the best order in which to print them. It currently does this by finding the closest next polygon to print.
|
||||
* Infill: This code generates a group of lines from an area. This is the code that generates the actuall infill pattern. There is also a concentric infill function, which is currently not used.
|
||||
* Comb: The combing code is the code that tries to avoid holes when moving around the head without printing. This code also detects when it fails. The final GCode generator uses the combing code while generating the final GCode. So they interact closely.
|
||||
* GCodeExport: The GCode export is a 2 step process. First it collects all the paths for a layer that it needs to print, this includes all moves, prints, extrusion widths. And then it generates the final GCode. This is the only piece of code that has knowledge about GCode, and to generate a different flavor of GCode it will be the only piece that needs adjustment. All volumatric calculations also happen here.
|
||||
+1325
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
@@ -0,0 +1,249 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_ALLOCATORS_H_
|
||||
#define RAPIDJSON_ALLOCATORS_H_
|
||||
|
||||
#include "rapidjson.h"
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Allocator
|
||||
|
||||
/*! \class rapidjson::Allocator
|
||||
\brief Concept for allocating, resizing and freeing memory block.
|
||||
|
||||
Note that Malloc() and Realloc() are non-static but Free() is static.
|
||||
|
||||
So if an allocator need to support Free(), it needs to put its pointer in
|
||||
the header of memory block.
|
||||
|
||||
\code
|
||||
concept Allocator {
|
||||
static const bool kNeedFree; //!< Whether this allocator needs to call Free().
|
||||
|
||||
// Allocate a memory block.
|
||||
// \param size of the memory block in bytes.
|
||||
// \returns pointer to the memory block.
|
||||
void* Malloc(size_t size);
|
||||
|
||||
// Resize a memory block.
|
||||
// \param originalPtr The pointer to current memory block. Null pointer is permitted.
|
||||
// \param originalSize The current size in bytes. (Design issue: since some allocator may not book-keep this, explicitly pass to it can save memory.)
|
||||
// \param newSize the new size in bytes.
|
||||
void* Realloc(void* originalPtr, size_t originalSize, size_t newSize);
|
||||
|
||||
// Free a memory block.
|
||||
// \param pointer to the memory block. Null pointer is permitted.
|
||||
static void Free(void *ptr);
|
||||
};
|
||||
\endcode
|
||||
*/
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// CrtAllocator
|
||||
|
||||
//! C-runtime library allocator.
|
||||
/*! This class is just wrapper for standard C library memory routines.
|
||||
\note implements Allocator concept
|
||||
*/
|
||||
class CrtAllocator {
|
||||
public:
|
||||
static const bool kNeedFree = true;
|
||||
void* Malloc(size_t size) {
|
||||
if (size) // behavior of malloc(0) is implementation defined.
|
||||
return std::malloc(size);
|
||||
else
|
||||
return NULL; // standardize to returning NULL.
|
||||
}
|
||||
void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) { (void)originalSize; return std::realloc(originalPtr, newSize); }
|
||||
static void Free(void *ptr) { std::free(ptr); }
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// MemoryPoolAllocator
|
||||
|
||||
//! Default memory allocator used by the parser and DOM.
|
||||
/*! This allocator allocate memory blocks from pre-allocated memory chunks.
|
||||
|
||||
It does not free memory blocks. And Realloc() only allocate new memory.
|
||||
|
||||
The memory chunks are allocated by BaseAllocator, which is CrtAllocator by default.
|
||||
|
||||
User may also supply a buffer as the first chunk.
|
||||
|
||||
If the user-buffer is full then additional chunks are allocated by BaseAllocator.
|
||||
|
||||
The user-buffer is not deallocated by this allocator.
|
||||
|
||||
\tparam BaseAllocator the allocator type for allocating memory chunks. Default is CrtAllocator.
|
||||
\note implements Allocator concept
|
||||
*/
|
||||
template <typename BaseAllocator = CrtAllocator>
|
||||
class MemoryPoolAllocator {
|
||||
public:
|
||||
static const bool kNeedFree = false; //!< Tell users that no need to call Free() with this allocator. (concept Allocator)
|
||||
|
||||
//! Constructor with chunkSize.
|
||||
/*! \param chunkSize The size of memory chunk. The default is kDefaultChunkSize.
|
||||
\param baseAllocator The allocator for allocating memory chunks.
|
||||
*/
|
||||
MemoryPoolAllocator(size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) :
|
||||
chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(0), baseAllocator_(baseAllocator), ownBaseAllocator_(0)
|
||||
{
|
||||
}
|
||||
|
||||
//! Constructor with user-supplied buffer.
|
||||
/*! The user buffer will be used firstly. When it is full, memory pool allocates new chunk with chunk size.
|
||||
|
||||
The user buffer will not be deallocated when this allocator is destructed.
|
||||
|
||||
\param buffer User supplied buffer.
|
||||
\param size Size of the buffer in bytes. It must at least larger than sizeof(ChunkHeader).
|
||||
\param chunkSize The size of memory chunk. The default is kDefaultChunkSize.
|
||||
\param baseAllocator The allocator for allocating memory chunks.
|
||||
*/
|
||||
MemoryPoolAllocator(void *buffer, size_t size, size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) :
|
||||
chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(buffer), baseAllocator_(baseAllocator), ownBaseAllocator_(0)
|
||||
{
|
||||
RAPIDJSON_ASSERT(buffer != 0);
|
||||
RAPIDJSON_ASSERT(size > sizeof(ChunkHeader));
|
||||
chunkHead_ = reinterpret_cast<ChunkHeader*>(buffer);
|
||||
chunkHead_->capacity = size - sizeof(ChunkHeader);
|
||||
chunkHead_->size = 0;
|
||||
chunkHead_->next = 0;
|
||||
}
|
||||
|
||||
//! Destructor.
|
||||
/*! This deallocates all memory chunks, excluding the user-supplied buffer.
|
||||
*/
|
||||
~MemoryPoolAllocator() {
|
||||
Clear();
|
||||
RAPIDJSON_DELETE(ownBaseAllocator_);
|
||||
}
|
||||
|
||||
//! Deallocates all memory chunks, excluding the user-supplied buffer.
|
||||
void Clear() {
|
||||
while(chunkHead_ != 0 && chunkHead_ != userBuffer_) {
|
||||
ChunkHeader* next = chunkHead_->next;
|
||||
baseAllocator_->Free(chunkHead_);
|
||||
chunkHead_ = next;
|
||||
}
|
||||
}
|
||||
|
||||
//! Computes the total capacity of allocated memory chunks.
|
||||
/*! \return total capacity in bytes.
|
||||
*/
|
||||
size_t Capacity() const {
|
||||
size_t capacity = 0;
|
||||
for (ChunkHeader* c = chunkHead_; c != 0; c = c->next)
|
||||
capacity += c->capacity;
|
||||
return capacity;
|
||||
}
|
||||
|
||||
//! Computes the memory blocks allocated.
|
||||
/*! \return total used bytes.
|
||||
*/
|
||||
size_t Size() const {
|
||||
size_t size = 0;
|
||||
for (ChunkHeader* c = chunkHead_; c != 0; c = c->next)
|
||||
size += c->size;
|
||||
return size;
|
||||
}
|
||||
|
||||
//! Allocates a memory block. (concept Allocator)
|
||||
void* Malloc(size_t size) {
|
||||
if (!size)
|
||||
return NULL;
|
||||
|
||||
size = RAPIDJSON_ALIGN(size);
|
||||
if (chunkHead_ == 0 || chunkHead_->size + size > chunkHead_->capacity)
|
||||
AddChunk(chunk_capacity_ > size ? chunk_capacity_ : size);
|
||||
|
||||
void *buffer = reinterpret_cast<char *>(chunkHead_ + 1) + chunkHead_->size;
|
||||
chunkHead_->size += size;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
//! Resizes a memory block (concept Allocator)
|
||||
void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) {
|
||||
if (originalPtr == 0)
|
||||
return Malloc(newSize);
|
||||
|
||||
// Do not shrink if new size is smaller than original
|
||||
if (originalSize >= newSize)
|
||||
return originalPtr;
|
||||
|
||||
// Simply expand it if it is the last allocation and there is sufficient space
|
||||
if (originalPtr == (char *)(chunkHead_ + 1) + chunkHead_->size - originalSize) {
|
||||
size_t increment = static_cast<size_t>(newSize - originalSize);
|
||||
increment = RAPIDJSON_ALIGN(increment);
|
||||
if (chunkHead_->size + increment <= chunkHead_->capacity) {
|
||||
chunkHead_->size += increment;
|
||||
return originalPtr;
|
||||
}
|
||||
}
|
||||
|
||||
// Realloc process: allocate and copy memory, do not free original buffer.
|
||||
void* newBuffer = Malloc(newSize);
|
||||
RAPIDJSON_ASSERT(newBuffer != 0); // Do not handle out-of-memory explicitly.
|
||||
if (originalSize)
|
||||
std::memcpy(newBuffer, originalPtr, originalSize);
|
||||
return newBuffer;
|
||||
}
|
||||
|
||||
//! Frees a memory block (concept Allocator)
|
||||
static void Free(void *ptr) { (void)ptr; } // Do nothing
|
||||
|
||||
private:
|
||||
//! Copy constructor is not permitted.
|
||||
MemoryPoolAllocator(const MemoryPoolAllocator& rhs) /* = delete */;
|
||||
//! Copy assignment operator is not permitted.
|
||||
MemoryPoolAllocator& operator=(const MemoryPoolAllocator& rhs) /* = delete */;
|
||||
|
||||
//! Creates a new chunk.
|
||||
/*! \param capacity Capacity of the chunk in bytes.
|
||||
*/
|
||||
void AddChunk(size_t capacity) {
|
||||
if (!baseAllocator_)
|
||||
ownBaseAllocator_ = baseAllocator_ = RAPIDJSON_NEW(BaseAllocator());
|
||||
ChunkHeader* chunk = reinterpret_cast<ChunkHeader*>(baseAllocator_->Malloc(sizeof(ChunkHeader) + capacity));
|
||||
chunk->capacity = capacity;
|
||||
chunk->size = 0;
|
||||
chunk->next = chunkHead_;
|
||||
chunkHead_ = chunk;
|
||||
}
|
||||
|
||||
static const int kDefaultChunkCapacity = 64 * 1024; //!< Default chunk capacity.
|
||||
|
||||
//! Chunk header for perpending to each chunk.
|
||||
/*! Chunks are stored as a singly linked list.
|
||||
*/
|
||||
struct ChunkHeader {
|
||||
size_t capacity; //!< Capacity of the chunk in bytes (excluding the header itself).
|
||||
size_t size; //!< Current size of allocated memory in bytes.
|
||||
ChunkHeader *next; //!< Next chunk in the linked list.
|
||||
};
|
||||
|
||||
ChunkHeader *chunkHead_; //!< Head of the chunk linked-list. Only the head chunk serves allocation.
|
||||
size_t chunk_capacity_; //!< The minimum capacity of chunk when they are allocated.
|
||||
void *userBuffer_; //!< User supplied buffer.
|
||||
BaseAllocator* baseAllocator_; //!< base allocator for allocating memory chunks.
|
||||
BaseAllocator* ownBaseAllocator_; //!< base allocator created by this object.
|
||||
};
|
||||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_ENCODINGS_H_
|
||||
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
@@ -0,0 +1,261 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_ENCODEDSTREAM_H_
|
||||
#define RAPIDJSON_ENCODEDSTREAM_H_
|
||||
|
||||
#include "rapidjson.h"
|
||||
|
||||
#ifdef __GNUC__
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(effc++)
|
||||
#endif
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
|
||||
//! Input byte stream wrapper with a statically bound encoding.
|
||||
/*!
|
||||
\tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE.
|
||||
\tparam InputByteStream Type of input byte stream. For example, FileReadStream.
|
||||
*/
|
||||
template <typename Encoding, typename InputByteStream>
|
||||
class EncodedInputStream {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
public:
|
||||
typedef typename Encoding::Ch Ch;
|
||||
|
||||
EncodedInputStream(InputByteStream& is) : is_(is) {
|
||||
current_ = Encoding::TakeBOM(is_);
|
||||
}
|
||||
|
||||
Ch Peek() const { return current_; }
|
||||
Ch Take() { Ch c = current_; current_ = Encoding::Take(is_); return c; }
|
||||
size_t Tell() const { return is_.Tell(); }
|
||||
|
||||
// Not implemented
|
||||
void Put(Ch) { RAPIDJSON_ASSERT(false); }
|
||||
void Flush() { RAPIDJSON_ASSERT(false); }
|
||||
Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
|
||||
size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
|
||||
|
||||
private:
|
||||
EncodedInputStream(const EncodedInputStream&);
|
||||
EncodedInputStream& operator=(const EncodedInputStream&);
|
||||
|
||||
InputByteStream& is_;
|
||||
Ch current_;
|
||||
};
|
||||
|
||||
//! Output byte stream wrapper with statically bound encoding.
|
||||
/*!
|
||||
\tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE.
|
||||
\tparam InputByteStream Type of input byte stream. For example, FileWriteStream.
|
||||
*/
|
||||
template <typename Encoding, typename OutputByteStream>
|
||||
class EncodedOutputStream {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
public:
|
||||
typedef typename Encoding::Ch Ch;
|
||||
|
||||
EncodedOutputStream(OutputByteStream& os, bool putBOM = true) : os_(os) {
|
||||
if (putBOM)
|
||||
Encoding::PutBOM(os_);
|
||||
}
|
||||
|
||||
void Put(Ch c) { Encoding::Put(os_, c); }
|
||||
void Flush() { os_.Flush(); }
|
||||
|
||||
// Not implemented
|
||||
Ch Peek() const { RAPIDJSON_ASSERT(false); }
|
||||
Ch Take() { RAPIDJSON_ASSERT(false); }
|
||||
size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; }
|
||||
Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
|
||||
size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
|
||||
|
||||
private:
|
||||
EncodedOutputStream(const EncodedOutputStream&);
|
||||
EncodedOutputStream& operator=(const EncodedOutputStream&);
|
||||
|
||||
OutputByteStream& os_;
|
||||
};
|
||||
|
||||
#define RAPIDJSON_ENCODINGS_FUNC(x) UTF8<Ch>::x, UTF16LE<Ch>::x, UTF16BE<Ch>::x, UTF32LE<Ch>::x, UTF32BE<Ch>::x
|
||||
|
||||
//! Input stream wrapper with dynamically bound encoding and automatic encoding detection.
|
||||
/*!
|
||||
\tparam CharType Type of character for reading.
|
||||
\tparam InputByteStream type of input byte stream to be wrapped.
|
||||
*/
|
||||
template <typename CharType, typename InputByteStream>
|
||||
class AutoUTFInputStream {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
public:
|
||||
typedef CharType Ch;
|
||||
|
||||
//! Constructor.
|
||||
/*!
|
||||
\param is input stream to be wrapped.
|
||||
\param type UTF encoding type if it is not detected from the stream.
|
||||
*/
|
||||
AutoUTFInputStream(InputByteStream& is, UTFType type = kUTF8) : is_(&is), type_(type), hasBOM_(false) {
|
||||
RAPIDJSON_ASSERT(type >= kUTF8 && type <= kUTF32BE);
|
||||
DetectType();
|
||||
static const TakeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Take) };
|
||||
takeFunc_ = f[type_];
|
||||
current_ = takeFunc_(*is_);
|
||||
}
|
||||
|
||||
UTFType GetType() const { return type_; }
|
||||
bool HasBOM() const { return hasBOM_; }
|
||||
|
||||
Ch Peek() const { return current_; }
|
||||
Ch Take() { Ch c = current_; current_ = takeFunc_(*is_); return c; }
|
||||
size_t Tell() const { return is_->Tell(); }
|
||||
|
||||
// Not implemented
|
||||
void Put(Ch) { RAPIDJSON_ASSERT(false); }
|
||||
void Flush() { RAPIDJSON_ASSERT(false); }
|
||||
Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
|
||||
size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
|
||||
|
||||
private:
|
||||
AutoUTFInputStream(const AutoUTFInputStream&);
|
||||
AutoUTFInputStream& operator=(const AutoUTFInputStream&);
|
||||
|
||||
// Detect encoding type with BOM or RFC 4627
|
||||
void DetectType() {
|
||||
// BOM (Byte Order Mark):
|
||||
// 00 00 FE FF UTF-32BE
|
||||
// FF FE 00 00 UTF-32LE
|
||||
// FE FF UTF-16BE
|
||||
// FF FE UTF-16LE
|
||||
// EF BB BF UTF-8
|
||||
|
||||
const unsigned char* c = (const unsigned char *)is_->Peek4();
|
||||
if (!c)
|
||||
return;
|
||||
|
||||
unsigned bom = c[0] | (c[1] << 8) | (c[2] << 16) | (c[3] << 24);
|
||||
hasBOM_ = false;
|
||||
if (bom == 0xFFFE0000) { type_ = kUTF32BE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); }
|
||||
else if (bom == 0x0000FEFF) { type_ = kUTF32LE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); }
|
||||
else if ((bom & 0xFFFF) == 0xFFFE) { type_ = kUTF16BE; hasBOM_ = true; is_->Take(); is_->Take(); }
|
||||
else if ((bom & 0xFFFF) == 0xFEFF) { type_ = kUTF16LE; hasBOM_ = true; is_->Take(); is_->Take(); }
|
||||
else if ((bom & 0xFFFFFF) == 0xBFBBEF) { type_ = kUTF8; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); }
|
||||
|
||||
// RFC 4627: Section 3
|
||||
// "Since the first two characters of a JSON text will always be ASCII
|
||||
// characters [RFC0020], it is possible to determine whether an octet
|
||||
// stream is UTF-8, UTF-16 (BE or LE), or UTF-32 (BE or LE) by looking
|
||||
// at the pattern of nulls in the first four octets."
|
||||
// 00 00 00 xx UTF-32BE
|
||||
// 00 xx 00 xx UTF-16BE
|
||||
// xx 00 00 00 UTF-32LE
|
||||
// xx 00 xx 00 UTF-16LE
|
||||
// xx xx xx xx UTF-8
|
||||
|
||||
if (!hasBOM_) {
|
||||
unsigned pattern = (c[0] ? 1 : 0) | (c[1] ? 2 : 0) | (c[2] ? 4 : 0) | (c[3] ? 8 : 0);
|
||||
switch (pattern) {
|
||||
case 0x08: type_ = kUTF32BE; break;
|
||||
case 0x0A: type_ = kUTF16BE; break;
|
||||
case 0x01: type_ = kUTF32LE; break;
|
||||
case 0x05: type_ = kUTF16LE; break;
|
||||
case 0x0F: type_ = kUTF8; break;
|
||||
default: break; // Use type defined by user.
|
||||
}
|
||||
}
|
||||
|
||||
// Runtime check whether the size of character type is sufficient. It only perform checks with assertion.
|
||||
if (type_ == kUTF16LE || type_ == kUTF16BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 2);
|
||||
if (type_ == kUTF32LE || type_ == kUTF32BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 4);
|
||||
}
|
||||
|
||||
typedef Ch (*TakeFunc)(InputByteStream& is);
|
||||
InputByteStream* is_;
|
||||
UTFType type_;
|
||||
Ch current_;
|
||||
TakeFunc takeFunc_;
|
||||
bool hasBOM_;
|
||||
};
|
||||
|
||||
//! Output stream wrapper with dynamically bound encoding and automatic encoding detection.
|
||||
/*!
|
||||
\tparam CharType Type of character for writing.
|
||||
\tparam InputByteStream type of output byte stream to be wrapped.
|
||||
*/
|
||||
template <typename CharType, typename OutputByteStream>
|
||||
class AutoUTFOutputStream {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
public:
|
||||
typedef CharType Ch;
|
||||
|
||||
//! Constructor.
|
||||
/*!
|
||||
\param os output stream to be wrapped.
|
||||
\param type UTF encoding type.
|
||||
\param putBOM Whether to write BOM at the beginning of the stream.
|
||||
*/
|
||||
AutoUTFOutputStream(OutputByteStream& os, UTFType type, bool putBOM) : os_(&os), type_(type) {
|
||||
RAPIDJSON_ASSERT(type >= kUTF8 && type <= kUTF32BE);
|
||||
|
||||
// Runtime check whether the size of character type is sufficient. It only perform checks with assertion.
|
||||
if (type_ == kUTF16LE || type_ == kUTF16BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 2);
|
||||
if (type_ == kUTF32LE || type_ == kUTF32BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 4);
|
||||
|
||||
static const PutFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Put) };
|
||||
putFunc_ = f[type_];
|
||||
|
||||
if (putBOM)
|
||||
PutBOM();
|
||||
}
|
||||
|
||||
UTFType GetType() const { return type_; }
|
||||
|
||||
void Put(Ch c) { putFunc_(*os_, c); }
|
||||
void Flush() { os_->Flush(); }
|
||||
|
||||
// Not implemented
|
||||
Ch Peek() const { RAPIDJSON_ASSERT(false); }
|
||||
Ch Take() { RAPIDJSON_ASSERT(false); }
|
||||
size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; }
|
||||
Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
|
||||
size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
|
||||
|
||||
private:
|
||||
AutoUTFOutputStream(const AutoUTFOutputStream&);
|
||||
AutoUTFOutputStream& operator=(const AutoUTFOutputStream&);
|
||||
|
||||
void PutBOM() {
|
||||
typedef void (*PutBOMFunc)(OutputByteStream&);
|
||||
static const PutBOMFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(PutBOM) };
|
||||
f[type_](*os_);
|
||||
}
|
||||
|
||||
typedef void (*PutFunc)(OutputByteStream&, Ch);
|
||||
|
||||
OutputByteStream* os_;
|
||||
UTFType type_;
|
||||
PutFunc putFunc_;
|
||||
};
|
||||
|
||||
#undef RAPIDJSON_ENCODINGS_FUNC
|
||||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#ifdef __GNUC__
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
#endif // RAPIDJSON_FILESTREAM_H_
|
||||
@@ -0,0 +1,625 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_ENCODINGS_H_
|
||||
#define RAPIDJSON_ENCODINGS_H_
|
||||
|
||||
#include "rapidjson.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(4244) // conversion from 'type1' to 'type2', possible loss of data
|
||||
RAPIDJSON_DIAG_OFF(4702) // unreachable code
|
||||
#elif defined(__GNUC__)
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(effc++)
|
||||
RAPIDJSON_DIAG_OFF(overflow)
|
||||
#endif
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Encoding
|
||||
|
||||
/*! \class rapidjson::Encoding
|
||||
\brief Concept for encoding of Unicode characters.
|
||||
|
||||
\code
|
||||
concept Encoding {
|
||||
typename Ch; //! Type of character. A "character" is actually a code unit in unicode's definition.
|
||||
|
||||
enum { supportUnicode = 1 }; // or 0 if not supporting unicode
|
||||
|
||||
//! \brief Encode a Unicode codepoint to an output stream.
|
||||
//! \param os Output stream.
|
||||
//! \param codepoint An unicode codepoint, ranging from 0x0 to 0x10FFFF inclusively.
|
||||
template<typename OutputStream>
|
||||
static void Encode(OutputStream& os, unsigned codepoint);
|
||||
|
||||
//! \brief Decode a Unicode codepoint from an input stream.
|
||||
//! \param is Input stream.
|
||||
//! \param codepoint Output of the unicode codepoint.
|
||||
//! \return true if a valid codepoint can be decoded from the stream.
|
||||
template <typename InputStream>
|
||||
static bool Decode(InputStream& is, unsigned* codepoint);
|
||||
|
||||
//! \brief Validate one Unicode codepoint from an encoded stream.
|
||||
//! \param is Input stream to obtain codepoint.
|
||||
//! \param os Output for copying one codepoint.
|
||||
//! \return true if it is valid.
|
||||
//! \note This function just validating and copying the codepoint without actually decode it.
|
||||
template <typename InputStream, typename OutputStream>
|
||||
static bool Validate(InputStream& is, OutputStream& os);
|
||||
|
||||
// The following functions are deal with byte streams.
|
||||
|
||||
//! Take a character from input byte stream, skip BOM if exist.
|
||||
template <typename InputByteStream>
|
||||
static CharType TakeBOM(InputByteStream& is);
|
||||
|
||||
//! Take a character from input byte stream.
|
||||
template <typename InputByteStream>
|
||||
static Ch Take(InputByteStream& is);
|
||||
|
||||
//! Put BOM to output byte stream.
|
||||
template <typename OutputByteStream>
|
||||
static void PutBOM(OutputByteStream& os);
|
||||
|
||||
//! Put a character to output byte stream.
|
||||
template <typename OutputByteStream>
|
||||
static void Put(OutputByteStream& os, Ch c);
|
||||
};
|
||||
\endcode
|
||||
*/
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// UTF8
|
||||
|
||||
//! UTF-8 encoding.
|
||||
/*! http://en.wikipedia.org/wiki/UTF-8
|
||||
http://tools.ietf.org/html/rfc3629
|
||||
\tparam CharType Code unit for storing 8-bit UTF-8 data. Default is char.
|
||||
\note implements Encoding concept
|
||||
*/
|
||||
template<typename CharType = char>
|
||||
struct UTF8 {
|
||||
typedef CharType Ch;
|
||||
|
||||
enum { supportUnicode = 1 };
|
||||
|
||||
template<typename OutputStream>
|
||||
static void Encode(OutputStream& os, unsigned codepoint) {
|
||||
if (codepoint <= 0x7F)
|
||||
os.Put(static_cast<Ch>(codepoint & 0xFF));
|
||||
else if (codepoint <= 0x7FF) {
|
||||
os.Put(static_cast<Ch>(0xC0 | ((codepoint >> 6) & 0xFF)));
|
||||
os.Put(static_cast<Ch>(0x80 | ((codepoint & 0x3F))));
|
||||
}
|
||||
else if (codepoint <= 0xFFFF) {
|
||||
os.Put(static_cast<Ch>(0xE0 | ((codepoint >> 12) & 0xFF)));
|
||||
os.Put(static_cast<Ch>(0x80 | ((codepoint >> 6) & 0x3F)));
|
||||
os.Put(static_cast<Ch>(0x80 | (codepoint & 0x3F)));
|
||||
}
|
||||
else {
|
||||
RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
|
||||
os.Put(static_cast<Ch>(0xF0 | ((codepoint >> 18) & 0xFF)));
|
||||
os.Put(static_cast<Ch>(0x80 | ((codepoint >> 12) & 0x3F)));
|
||||
os.Put(static_cast<Ch>(0x80 | ((codepoint >> 6) & 0x3F)));
|
||||
os.Put(static_cast<Ch>(0x80 | (codepoint & 0x3F)));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename InputStream>
|
||||
static bool Decode(InputStream& is, unsigned* codepoint) {
|
||||
#define COPY() c = is.Take(); *codepoint = (*codepoint << 6) | ((unsigned char)c & 0x3Fu)
|
||||
#define TRANS(mask) result &= ((GetRange((unsigned char)c) & mask) != 0)
|
||||
#define TAIL() COPY(); TRANS(0x70)
|
||||
Ch c = is.Take();
|
||||
if (!(c & 0x80)) {
|
||||
*codepoint = (unsigned char)c;
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned char type = GetRange((unsigned char)c);
|
||||
*codepoint = (0xFF >> type) & (unsigned char)c;
|
||||
bool result = true;
|
||||
switch (type) {
|
||||
case 2: TAIL(); return result;
|
||||
case 3: TAIL(); TAIL(); return result;
|
||||
case 4: COPY(); TRANS(0x50); TAIL(); return result;
|
||||
case 5: COPY(); TRANS(0x10); TAIL(); TAIL(); return result;
|
||||
case 6: TAIL(); TAIL(); TAIL(); return result;
|
||||
case 10: COPY(); TRANS(0x20); TAIL(); return result;
|
||||
case 11: COPY(); TRANS(0x60); TAIL(); TAIL(); return result;
|
||||
default: return false;
|
||||
}
|
||||
#undef COPY
|
||||
#undef TRANS
|
||||
#undef TAIL
|
||||
}
|
||||
|
||||
template <typename InputStream, typename OutputStream>
|
||||
static bool Validate(InputStream& is, OutputStream& os) {
|
||||
#define COPY() os.Put(c = is.Take())
|
||||
#define TRANS(mask) result &= ((GetRange((unsigned char)c) & mask) != 0)
|
||||
#define TAIL() COPY(); TRANS(0x70)
|
||||
Ch c;
|
||||
COPY();
|
||||
if (!(c & 0x80))
|
||||
return true;
|
||||
|
||||
bool result = true;
|
||||
switch (GetRange((unsigned char)c)) {
|
||||
case 2: TAIL(); return result;
|
||||
case 3: TAIL(); TAIL(); return result;
|
||||
case 4: COPY(); TRANS(0x50); TAIL(); return result;
|
||||
case 5: COPY(); TRANS(0x10); TAIL(); TAIL(); return result;
|
||||
case 6: TAIL(); TAIL(); TAIL(); return result;
|
||||
case 10: COPY(); TRANS(0x20); TAIL(); return result;
|
||||
case 11: COPY(); TRANS(0x60); TAIL(); TAIL(); return result;
|
||||
default: return false;
|
||||
}
|
||||
#undef COPY
|
||||
#undef TRANS
|
||||
#undef TAIL
|
||||
}
|
||||
|
||||
static unsigned char GetRange(unsigned char c) {
|
||||
// Referring to DFA of http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
|
||||
// With new mapping 1 -> 0x10, 7 -> 0x20, 9 -> 0x40, such that AND operation can test multiple types.
|
||||
static const unsigned char type[] = {
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,
|
||||
0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,
|
||||
0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
|
||||
0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
|
||||
8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
|
||||
10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8,
|
||||
};
|
||||
return type[c];
|
||||
}
|
||||
|
||||
template <typename InputByteStream>
|
||||
static CharType TakeBOM(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
Ch c = Take(is);
|
||||
if ((unsigned char)c != 0xEFu) return c;
|
||||
c = is.Take();
|
||||
if ((unsigned char)c != 0xBBu) return c;
|
||||
c = is.Take();
|
||||
if ((unsigned char)c != 0xBFu) return c;
|
||||
c = is.Take();
|
||||
return c;
|
||||
}
|
||||
|
||||
template <typename InputByteStream>
|
||||
static Ch Take(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
return is.Take();
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void PutBOM(OutputByteStream& os) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
os.Put(0xEFu); os.Put(0xBBu); os.Put(0xBFu);
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void Put(OutputByteStream& os, Ch c) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>(c));
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// UTF16
|
||||
|
||||
//! UTF-16 encoding.
|
||||
/*! http://en.wikipedia.org/wiki/UTF-16
|
||||
http://tools.ietf.org/html/rfc2781
|
||||
\tparam CharType Type for storing 16-bit UTF-16 data. Default is wchar_t. C++11 may use char16_t instead.
|
||||
\note implements Encoding concept
|
||||
|
||||
\note For in-memory access, no need to concern endianness. The code units and code points are represented by CPU's endianness.
|
||||
For streaming, use UTF16LE and UTF16BE, which handle endianness.
|
||||
*/
|
||||
template<typename CharType = wchar_t>
|
||||
struct UTF16 {
|
||||
typedef CharType Ch;
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 2);
|
||||
|
||||
enum { supportUnicode = 1 };
|
||||
|
||||
template<typename OutputStream>
|
||||
static void Encode(OutputStream& os, unsigned codepoint) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2);
|
||||
if (codepoint <= 0xFFFF) {
|
||||
RAPIDJSON_ASSERT(codepoint < 0xD800 || codepoint > 0xDFFF); // Code point itself cannot be surrogate pair
|
||||
os.Put(static_cast<typename OutputStream::Ch>(codepoint));
|
||||
}
|
||||
else {
|
||||
RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
|
||||
unsigned v = codepoint - 0x10000;
|
||||
os.Put(static_cast<typename OutputStream::Ch>((v >> 10) | 0xD800));
|
||||
os.Put((v & 0x3FF) | 0xDC00);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename InputStream>
|
||||
static bool Decode(InputStream& is, unsigned* codepoint) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2);
|
||||
Ch c = is.Take();
|
||||
if (c < 0xD800 || c > 0xDFFF) {
|
||||
*codepoint = c;
|
||||
return true;
|
||||
}
|
||||
else if (c <= 0xDBFF) {
|
||||
*codepoint = (c & 0x3FF) << 10;
|
||||
c = is.Take();
|
||||
*codepoint |= (c & 0x3FF);
|
||||
*codepoint += 0x10000;
|
||||
return c >= 0xDC00 && c <= 0xDFFF;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename InputStream, typename OutputStream>
|
||||
static bool Validate(InputStream& is, OutputStream& os) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2);
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2);
|
||||
Ch c;
|
||||
os.Put(c = is.Take());
|
||||
if (c < 0xD800 || c > 0xDFFF)
|
||||
return true;
|
||||
else if (c <= 0xDBFF) {
|
||||
os.Put(c = is.Take());
|
||||
return c >= 0xDC00 && c <= 0xDFFF;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
//! UTF-16 little endian encoding.
|
||||
template<typename CharType = wchar_t>
|
||||
struct UTF16LE : UTF16<CharType> {
|
||||
template <typename InputByteStream>
|
||||
static CharType TakeBOM(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
CharType c = Take(is);
|
||||
return (unsigned short)c == 0xFEFFu ? Take(is) : c;
|
||||
}
|
||||
|
||||
template <typename InputByteStream>
|
||||
static CharType Take(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
CharType c = (unsigned char)is.Take();
|
||||
c |= (unsigned char)is.Take() << 8;
|
||||
return c;
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void PutBOM(OutputByteStream& os) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
os.Put(0xFFu); os.Put(0xFEu);
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void Put(OutputByteStream& os, CharType c) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
os.Put(c & 0xFFu);
|
||||
os.Put((c >> 8) & 0xFFu);
|
||||
}
|
||||
};
|
||||
|
||||
//! UTF-16 big endian encoding.
|
||||
template<typename CharType = wchar_t>
|
||||
struct UTF16BE : UTF16<CharType> {
|
||||
template <typename InputByteStream>
|
||||
static CharType TakeBOM(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
CharType c = Take(is);
|
||||
return (unsigned short)c == 0xFEFFu ? Take(is) : c;
|
||||
}
|
||||
|
||||
template <typename InputByteStream>
|
||||
static CharType Take(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
CharType c = (unsigned char)is.Take() << 8;
|
||||
c |= (unsigned char)is.Take();
|
||||
return c;
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void PutBOM(OutputByteStream& os) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
os.Put(0xFEu); os.Put(0xFFu);
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void Put(OutputByteStream& os, CharType c) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
os.Put((c >> 8) & 0xFFu);
|
||||
os.Put(c & 0xFFu);
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// UTF32
|
||||
|
||||
//! UTF-32 encoding.
|
||||
/*! http://en.wikipedia.org/wiki/UTF-32
|
||||
\tparam CharType Type for storing 32-bit UTF-32 data. Default is unsigned. C++11 may use char32_t instead.
|
||||
\note implements Encoding concept
|
||||
|
||||
\note For in-memory access, no need to concern endianness. The code units and code points are represented by CPU's endianness.
|
||||
For streaming, use UTF32LE and UTF32BE, which handle endianness.
|
||||
*/
|
||||
template<typename CharType = unsigned>
|
||||
struct UTF32 {
|
||||
typedef CharType Ch;
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 4);
|
||||
|
||||
enum { supportUnicode = 1 };
|
||||
|
||||
template<typename OutputStream>
|
||||
static void Encode(OutputStream& os, unsigned codepoint) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 4);
|
||||
RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
|
||||
os.Put(codepoint);
|
||||
}
|
||||
|
||||
template <typename InputStream>
|
||||
static bool Decode(InputStream& is, unsigned* codepoint) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4);
|
||||
Ch c = is.Take();
|
||||
*codepoint = c;
|
||||
return c <= 0x10FFFF;
|
||||
}
|
||||
|
||||
template <typename InputStream, typename OutputStream>
|
||||
static bool Validate(InputStream& is, OutputStream& os) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4);
|
||||
Ch c;
|
||||
os.Put(c = is.Take());
|
||||
return c <= 0x10FFFF;
|
||||
}
|
||||
};
|
||||
|
||||
//! UTF-32 little endian enocoding.
|
||||
template<typename CharType = unsigned>
|
||||
struct UTF32LE : UTF32<CharType> {
|
||||
template <typename InputByteStream>
|
||||
static CharType TakeBOM(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
CharType c = Take(is);
|
||||
return (unsigned)c == 0x0000FEFFu ? Take(is) : c;
|
||||
}
|
||||
|
||||
template <typename InputByteStream>
|
||||
static CharType Take(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
CharType c = (unsigned char)is.Take();
|
||||
c |= (unsigned char)is.Take() << 8;
|
||||
c |= (unsigned char)is.Take() << 16;
|
||||
c |= (unsigned char)is.Take() << 24;
|
||||
return c;
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void PutBOM(OutputByteStream& os) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
os.Put(0xFFu); os.Put(0xFEu); os.Put(0x00u); os.Put(0x00u);
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void Put(OutputByteStream& os, CharType c) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
os.Put(c & 0xFFu);
|
||||
os.Put((c >> 8) & 0xFFu);
|
||||
os.Put((c >> 16) & 0xFFu);
|
||||
os.Put((c >> 24) & 0xFFu);
|
||||
}
|
||||
};
|
||||
|
||||
//! UTF-32 big endian encoding.
|
||||
template<typename CharType = unsigned>
|
||||
struct UTF32BE : UTF32<CharType> {
|
||||
template <typename InputByteStream>
|
||||
static CharType TakeBOM(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
CharType c = Take(is);
|
||||
return (unsigned)c == 0x0000FEFFu ? Take(is) : c;
|
||||
}
|
||||
|
||||
template <typename InputByteStream>
|
||||
static CharType Take(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
CharType c = (unsigned char)is.Take() << 24;
|
||||
c |= (unsigned char)is.Take() << 16;
|
||||
c |= (unsigned char)is.Take() << 8;
|
||||
c |= (unsigned char)is.Take();
|
||||
return c;
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void PutBOM(OutputByteStream& os) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
os.Put(0x00u); os.Put(0x00u); os.Put(0xFEu); os.Put(0xFFu);
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void Put(OutputByteStream& os, CharType c) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
os.Put((c >> 24) & 0xFFu);
|
||||
os.Put((c >> 16) & 0xFFu);
|
||||
os.Put((c >> 8) & 0xFFu);
|
||||
os.Put(c & 0xFFu);
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// ASCII
|
||||
|
||||
//! ASCII encoding.
|
||||
/*! http://en.wikipedia.org/wiki/ASCII
|
||||
\tparam CharType Code unit for storing 7-bit ASCII data. Default is char.
|
||||
\note implements Encoding concept
|
||||
*/
|
||||
template<typename CharType = char>
|
||||
struct ASCII {
|
||||
typedef CharType Ch;
|
||||
|
||||
enum { supportUnicode = 0 };
|
||||
|
||||
template<typename OutputStream>
|
||||
static void Encode(OutputStream& os, unsigned codepoint) {
|
||||
RAPIDJSON_ASSERT(codepoint <= 0x7F);
|
||||
os.Put(static_cast<Ch>(codepoint & 0xFF));
|
||||
}
|
||||
|
||||
template <typename InputStream>
|
||||
static bool Decode(InputStream& is, unsigned* codepoint) {
|
||||
unsigned char c = static_cast<unsigned char>(is.Take());
|
||||
*codepoint = c;
|
||||
return c <= 0X7F;
|
||||
}
|
||||
|
||||
template <typename InputStream, typename OutputStream>
|
||||
static bool Validate(InputStream& is, OutputStream& os) {
|
||||
unsigned char c = is.Take();
|
||||
os.Put(c);
|
||||
return c <= 0x7F;
|
||||
}
|
||||
|
||||
template <typename InputByteStream>
|
||||
static CharType TakeBOM(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
Ch c = Take(is);
|
||||
return c;
|
||||
}
|
||||
|
||||
template <typename InputByteStream>
|
||||
static Ch Take(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
return is.Take();
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void PutBOM(OutputByteStream& os) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
(void)os;
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void Put(OutputByteStream& os, Ch c) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>(c));
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// AutoUTF
|
||||
|
||||
//! Runtime-specified UTF encoding type of a stream.
|
||||
enum UTFType {
|
||||
kUTF8 = 0, //!< UTF-8.
|
||||
kUTF16LE = 1, //!< UTF-16 little endian.
|
||||
kUTF16BE = 2, //!< UTF-16 big endian.
|
||||
kUTF32LE = 3, //!< UTF-32 little endian.
|
||||
kUTF32BE = 4 //!< UTF-32 big endian.
|
||||
};
|
||||
|
||||
//! Dynamically select encoding according to stream's runtime-specified UTF encoding type.
|
||||
/*! \note This class can be used with AutoUTFInputtStream and AutoUTFOutputStream, which provides GetType().
|
||||
*/
|
||||
template<typename CharType>
|
||||
struct AutoUTF {
|
||||
typedef CharType Ch;
|
||||
|
||||
enum { supportUnicode = 1 };
|
||||
|
||||
#define RAPIDJSON_ENCODINGS_FUNC(x) UTF8<Ch>::x, UTF16LE<Ch>::x, UTF16BE<Ch>::x, UTF32LE<Ch>::x, UTF32BE<Ch>::x
|
||||
|
||||
template<typename OutputStream>
|
||||
RAPIDJSON_FORCEINLINE static void Encode(OutputStream& os, unsigned codepoint) {
|
||||
typedef void (*EncodeFunc)(OutputStream&, unsigned);
|
||||
static const EncodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Encode) };
|
||||
(*f[os.GetType()])(os, codepoint);
|
||||
}
|
||||
|
||||
template <typename InputStream>
|
||||
RAPIDJSON_FORCEINLINE static bool Decode(InputStream& is, unsigned* codepoint) {
|
||||
typedef bool (*DecodeFunc)(InputStream&, unsigned*);
|
||||
static const DecodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Decode) };
|
||||
return (*f[is.GetType()])(is, codepoint);
|
||||
}
|
||||
|
||||
template <typename InputStream, typename OutputStream>
|
||||
RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) {
|
||||
typedef bool (*ValidateFunc)(InputStream&, OutputStream&);
|
||||
static const ValidateFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Validate) };
|
||||
return (*f[is.GetType()])(is, os);
|
||||
}
|
||||
|
||||
#undef RAPIDJSON_ENCODINGS_FUNC
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Transcoder
|
||||
|
||||
//! Encoding conversion.
|
||||
template<typename SourceEncoding, typename TargetEncoding>
|
||||
struct Transcoder {
|
||||
//! Take one Unicode codepoint from source encoding, convert it to target encoding and put it to the output stream.
|
||||
template<typename InputStream, typename OutputStream>
|
||||
RAPIDJSON_FORCEINLINE static bool Transcode(InputStream& is, OutputStream& os) {
|
||||
unsigned codepoint;
|
||||
if (!SourceEncoding::Decode(is, &codepoint))
|
||||
return false;
|
||||
TargetEncoding::Encode(os, codepoint);
|
||||
return true;
|
||||
}
|
||||
|
||||
//! Validate one Unicode codepoint from an encoded stream.
|
||||
template<typename InputStream, typename OutputStream>
|
||||
RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) {
|
||||
return Transcode(is, os); // Since source/target encoding is different, must transcode.
|
||||
}
|
||||
};
|
||||
|
||||
//! Specialization of Transcoder with same source and target encoding.
|
||||
template<typename Encoding>
|
||||
struct Transcoder<Encoding, Encoding> {
|
||||
template<typename InputStream, typename OutputStream>
|
||||
RAPIDJSON_FORCEINLINE static bool Transcode(InputStream& is, OutputStream& os) {
|
||||
os.Put(is.Take()); // Just copy one code unit. This semantic is different from primary template class.
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename InputStream, typename OutputStream>
|
||||
RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) {
|
||||
return Encoding::Validate(is, os); // source/target encoding are the same
|
||||
}
|
||||
};
|
||||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#if defined(__GNUC__) || defined(_MSV_VER)
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
#endif // RAPIDJSON_ENCODINGS_H_
|
||||
@@ -0,0 +1,65 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_ERROR_EN_H__
|
||||
#define RAPIDJSON_ERROR_EN_H__
|
||||
|
||||
#include "error.h"
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
|
||||
//! Maps error code of parsing into error message.
|
||||
/*!
|
||||
\ingroup RAPIDJSON_ERRORS
|
||||
\param parseErrorCode Error code obtained in parsing.
|
||||
\return the error message.
|
||||
\note User can make a copy of this function for localization.
|
||||
Using switch-case is safer for future modification of error codes.
|
||||
*/
|
||||
inline const RAPIDJSON_ERROR_CHARTYPE* GetParseError_En(ParseErrorCode parseErrorCode) {
|
||||
switch (parseErrorCode) {
|
||||
case kParseErrorNone: return RAPIDJSON_ERROR_STRING("No error.");
|
||||
|
||||
case kParseErrorDocumentEmpty: return RAPIDJSON_ERROR_STRING("The document is empty.");
|
||||
case kParseErrorDocumentRootNotSingular: return RAPIDJSON_ERROR_STRING("The document root must not follow by other values.");
|
||||
|
||||
case kParseErrorValueInvalid: return RAPIDJSON_ERROR_STRING("Invalid value.");
|
||||
|
||||
case kParseErrorObjectMissName: return RAPIDJSON_ERROR_STRING("Missing a name for object member.");
|
||||
case kParseErrorObjectMissColon: return RAPIDJSON_ERROR_STRING("Missing a colon after a name of object member.");
|
||||
case kParseErrorObjectMissCommaOrCurlyBracket: return RAPIDJSON_ERROR_STRING("Missing a comma or '}' after an object member.");
|
||||
|
||||
case kParseErrorArrayMissCommaOrSquareBracket: return RAPIDJSON_ERROR_STRING("Missing a comma or ']' after an array element.");
|
||||
|
||||
case kParseErrorStringUnicodeEscapeInvalidHex: return RAPIDJSON_ERROR_STRING("Incorrect hex digit after \\u escape in string.");
|
||||
case kParseErrorStringUnicodeSurrogateInvalid: return RAPIDJSON_ERROR_STRING("The surrogate pair in string is invalid.");
|
||||
case kParseErrorStringEscapeInvalid: return RAPIDJSON_ERROR_STRING("Invalid escape character in string.");
|
||||
case kParseErrorStringMissQuotationMark: return RAPIDJSON_ERROR_STRING("Missing a closing quotation mark in string.");
|
||||
case kParseErrorStringInvalidEncoding: return RAPIDJSON_ERROR_STRING("Invalid encoding in string.");
|
||||
|
||||
case kParseErrorNumberTooBig: return RAPIDJSON_ERROR_STRING("Number too big to be stored in double.");
|
||||
case kParseErrorNumberMissFraction: return RAPIDJSON_ERROR_STRING("Miss fraction part in number.");
|
||||
case kParseErrorNumberMissExponent: return RAPIDJSON_ERROR_STRING("Miss exponent in number.");
|
||||
|
||||
case kParseErrorTermination: return RAPIDJSON_ERROR_STRING("Terminate parsing due to Handler error.");
|
||||
case kParseErrorUnspecificSyntaxError: return RAPIDJSON_ERROR_STRING("Unspecific syntax error.");
|
||||
|
||||
default:
|
||||
return RAPIDJSON_ERROR_STRING("Unknown error.");
|
||||
}
|
||||
}
|
||||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_ERROR_EN_H__
|
||||
@@ -0,0 +1,144 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_ERROR_ERROR_H__
|
||||
#define RAPIDJSON_ERROR_ERROR_H__
|
||||
|
||||
/*! \file error.h */
|
||||
|
||||
/*! \defgroup RAPIDJSON_ERRORS RapidJSON error handling */
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// RAPIDJSON_ERROR_CHARTYPE
|
||||
|
||||
//! Character type of error messages.
|
||||
/*! \ingroup RAPIDJSON_ERRORS
|
||||
The default character type is \c char.
|
||||
On Windows, user can define this macro as \c TCHAR for supporting both
|
||||
unicode/non-unicode settings.
|
||||
*/
|
||||
#ifndef RAPIDJSON_ERROR_CHARTYPE
|
||||
#define RAPIDJSON_ERROR_CHARTYPE char
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// RAPIDJSON_ERROR_STRING
|
||||
|
||||
//! Macro for converting string literial to \ref RAPIDJSON_ERROR_CHARTYPE[].
|
||||
/*! \ingroup RAPIDJSON_ERRORS
|
||||
By default this conversion macro does nothing.
|
||||
On Windows, user can define this macro as \c _T(x) for supporting both
|
||||
unicode/non-unicode settings.
|
||||
*/
|
||||
#ifndef RAPIDJSON_ERROR_STRING
|
||||
#define RAPIDJSON_ERROR_STRING(x) x
|
||||
#endif
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// ParseErrorCode
|
||||
|
||||
//! Error code of parsing.
|
||||
/*! \ingroup RAPIDJSON_ERRORS
|
||||
\see GenericReader::Parse, GenericReader::GetParseErrorCode
|
||||
*/
|
||||
enum ParseErrorCode {
|
||||
kParseErrorNone = 0, //!< No error.
|
||||
|
||||
kParseErrorDocumentEmpty, //!< The document is empty.
|
||||
kParseErrorDocumentRootNotSingular, //!< The document root must not follow by other values.
|
||||
|
||||
kParseErrorValueInvalid, //!< Invalid value.
|
||||
|
||||
kParseErrorObjectMissName, //!< Missing a name for object member.
|
||||
kParseErrorObjectMissColon, //!< Missing a colon after a name of object member.
|
||||
kParseErrorObjectMissCommaOrCurlyBracket, //!< Missing a comma or '}' after an object member.
|
||||
|
||||
kParseErrorArrayMissCommaOrSquareBracket, //!< Missing a comma or ']' after an array element.
|
||||
|
||||
kParseErrorStringUnicodeEscapeInvalidHex, //!< Incorrect hex digit after \\u escape in string.
|
||||
kParseErrorStringUnicodeSurrogateInvalid, //!< The surrogate pair in string is invalid.
|
||||
kParseErrorStringEscapeInvalid, //!< Invalid escape character in string.
|
||||
kParseErrorStringMissQuotationMark, //!< Missing a closing quotation mark in string.
|
||||
kParseErrorStringInvalidEncoding, //!< Invalid encoding in string.
|
||||
|
||||
kParseErrorNumberTooBig, //!< Number too big to be stored in double.
|
||||
kParseErrorNumberMissFraction, //!< Miss fraction part in number.
|
||||
kParseErrorNumberMissExponent, //!< Miss exponent in number.
|
||||
|
||||
kParseErrorTermination, //!< Parsing was terminated.
|
||||
kParseErrorUnspecificSyntaxError //!< Unspecific syntax error.
|
||||
};
|
||||
|
||||
//! Result of parsing (wraps ParseErrorCode)
|
||||
/*!
|
||||
\ingroup RAPIDJSON_ERRORS
|
||||
\code
|
||||
Document doc;
|
||||
ParseResult ok = doc.Parse("[42]");
|
||||
if (!ok) {
|
||||
fprintf(stderr, "JSON parse error: %s (%u)",
|
||||
GetParseError_En(ok.Code()), ok.Offset());
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
\endcode
|
||||
\see GenericReader::Parse, GenericDocument::Parse
|
||||
*/
|
||||
struct ParseResult {
|
||||
|
||||
//! Default constructor, no error.
|
||||
ParseResult() : code_(kParseErrorNone), offset_(0) {}
|
||||
//! Constructor to set an error.
|
||||
ParseResult(ParseErrorCode code, size_t offset) : code_(code), offset_(offset) {}
|
||||
|
||||
//! Get the error code.
|
||||
ParseErrorCode Code() const { return code_; }
|
||||
//! Get the error offset, if \ref IsError(), 0 otherwise.
|
||||
size_t Offset() const { return offset_; }
|
||||
|
||||
//! Conversion to \c bool, returns \c true, iff !\ref IsError().
|
||||
operator bool() const { return !IsError(); }
|
||||
//! Whether the result is an error.
|
||||
bool IsError() const { return code_ != kParseErrorNone; }
|
||||
|
||||
bool operator==(const ParseResult& that) const { return code_ == that.code_; }
|
||||
bool operator==(ParseErrorCode code) const { return code_ == code; }
|
||||
friend bool operator==(ParseErrorCode code, const ParseResult & err) { return code == err.code_; }
|
||||
|
||||
//! Reset error code.
|
||||
void Clear() { Set(kParseErrorNone); }
|
||||
//! Update error code and offset.
|
||||
void Set(ParseErrorCode code, size_t offset = 0) { code_ = code; offset_ = offset; }
|
||||
|
||||
private:
|
||||
ParseErrorCode code_;
|
||||
size_t offset_;
|
||||
};
|
||||
|
||||
//! Function pointer type of GetParseError().
|
||||
/*! \ingroup RAPIDJSON_ERRORS
|
||||
|
||||
This is the prototype for \c GetParseError_X(), where \c X is a locale.
|
||||
User can dynamically change locale in runtime, e.g.:
|
||||
\code
|
||||
GetParseErrorFunc GetParseError = GetParseError_En; // or whatever
|
||||
const RAPIDJSON_ERROR_CHARTYPE* s = GetParseError(document.GetParseErrorCode());
|
||||
\endcode
|
||||
*/
|
||||
typedef const RAPIDJSON_ERROR_CHARTYPE* (*GetParseErrorFunc)(ParseErrorCode);
|
||||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_ERROR_ERROR_H__
|
||||
@@ -0,0 +1,88 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_FILEREADSTREAM_H_
|
||||
#define RAPIDJSON_FILEREADSTREAM_H_
|
||||
|
||||
#include "rapidjson.h"
|
||||
#include <cstdio>
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
|
||||
//! File byte stream for input using fread().
|
||||
/*!
|
||||
\note implements Stream concept
|
||||
*/
|
||||
class FileReadStream {
|
||||
public:
|
||||
typedef char Ch; //!< Character type (byte).
|
||||
|
||||
//! Constructor.
|
||||
/*!
|
||||
\param fp File pointer opened for read.
|
||||
\param buffer user-supplied buffer.
|
||||
\param bufferSize size of buffer in bytes. Must >=4 bytes.
|
||||
*/
|
||||
FileReadStream(std::FILE* fp, char* buffer, size_t bufferSize) : fp_(fp), buffer_(buffer), bufferSize_(bufferSize), bufferLast_(0), current_(buffer_), readCount_(0), count_(0), eof_(false) {
|
||||
RAPIDJSON_ASSERT(fp_ != 0);
|
||||
RAPIDJSON_ASSERT(bufferSize >= 4);
|
||||
Read();
|
||||
}
|
||||
|
||||
Ch Peek() const { return *current_; }
|
||||
Ch Take() { Ch c = *current_; Read(); return c; }
|
||||
size_t Tell() const { return count_ + static_cast<size_t>(current_ - buffer_); }
|
||||
|
||||
// Not implemented
|
||||
void Put(Ch) { RAPIDJSON_ASSERT(false); }
|
||||
void Flush() { RAPIDJSON_ASSERT(false); }
|
||||
Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
|
||||
size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
|
||||
|
||||
// For encoding detection only.
|
||||
const Ch* Peek4() const {
|
||||
return (current_ + 4 <= bufferLast_) ? current_ : 0;
|
||||
}
|
||||
|
||||
private:
|
||||
void Read() {
|
||||
if (current_ < bufferLast_)
|
||||
++current_;
|
||||
else if (!eof_) {
|
||||
count_ += readCount_;
|
||||
readCount_ = fread(buffer_, 1, bufferSize_, fp_);
|
||||
bufferLast_ = buffer_ + readCount_ - 1;
|
||||
current_ = buffer_;
|
||||
|
||||
if (readCount_ < bufferSize_) {
|
||||
buffer_[readCount_] = '\0';
|
||||
++bufferLast_;
|
||||
eof_ = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::FILE* fp_;
|
||||
Ch *buffer_;
|
||||
size_t bufferSize_;
|
||||
Ch *bufferLast_;
|
||||
Ch *current_;
|
||||
size_t readCount_;
|
||||
size_t count_; //!< Number of characters read
|
||||
bool eof_;
|
||||
};
|
||||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_FILESTREAM_H_
|
||||
@@ -0,0 +1,91 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_FILEWRITESTREAM_H_
|
||||
#define RAPIDJSON_FILEWRITESTREAM_H_
|
||||
|
||||
#include "rapidjson.h"
|
||||
#include <cstdio>
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
|
||||
//! Wrapper of C file stream for input using fread().
|
||||
/*!
|
||||
\note implements Stream concept
|
||||
*/
|
||||
class FileWriteStream {
|
||||
public:
|
||||
typedef char Ch; //!< Character type. Only support char.
|
||||
|
||||
FileWriteStream(std::FILE* fp, char* buffer, size_t bufferSize) : fp_(fp), buffer_(buffer), bufferEnd_(buffer + bufferSize), current_(buffer_) {
|
||||
RAPIDJSON_ASSERT(fp_ != 0);
|
||||
}
|
||||
|
||||
void Put(char c) {
|
||||
if (current_ >= bufferEnd_)
|
||||
Flush();
|
||||
|
||||
*current_++ = c;
|
||||
}
|
||||
|
||||
void PutN(char c, size_t n) {
|
||||
size_t avail = static_cast<size_t>(bufferEnd_ - current_);
|
||||
while (n > avail) {
|
||||
std::memset(current_, c, avail);
|
||||
current_ += avail;
|
||||
Flush();
|
||||
n -= avail;
|
||||
avail = static_cast<size_t>(bufferEnd_ - current_);
|
||||
}
|
||||
|
||||
if (n > 0) {
|
||||
std::memset(current_, c, n);
|
||||
current_ += n;
|
||||
}
|
||||
}
|
||||
|
||||
void Flush() {
|
||||
if (current_ != buffer_) {
|
||||
fwrite(buffer_, 1, static_cast<size_t>(current_ - buffer_), fp_);
|
||||
current_ = buffer_;
|
||||
}
|
||||
}
|
||||
|
||||
// Not implemented
|
||||
char Peek() const { RAPIDJSON_ASSERT(false); return 0; }
|
||||
char Take() { RAPIDJSON_ASSERT(false); return 0; }
|
||||
size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; }
|
||||
char* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
|
||||
size_t PutEnd(char*) { RAPIDJSON_ASSERT(false); return 0; }
|
||||
|
||||
private:
|
||||
// Prohibit copy constructor & assignment operator.
|
||||
FileWriteStream(const FileWriteStream&);
|
||||
FileWriteStream& operator=(const FileWriteStream&);
|
||||
|
||||
std::FILE* fp_;
|
||||
char *buffer_;
|
||||
char *bufferEnd_;
|
||||
char *current_;
|
||||
};
|
||||
|
||||
//! Implement specialized version of PutN() with memset() for better performance.
|
||||
template<>
|
||||
inline void PutN(FileWriteStream& stream, char c, size_t n) {
|
||||
stream.PutN(c, n);
|
||||
}
|
||||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_FILESTREAM_H_
|
||||
@@ -0,0 +1,280 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_BIGINTEGER_H_
|
||||
#define RAPIDJSON_BIGINTEGER_H_
|
||||
|
||||
#include "../rapidjson.h"
|
||||
|
||||
#if defined(_MSC_VER) && defined(_M_AMD64)
|
||||
#include <intrin.h> // for _umul128
|
||||
#endif
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
namespace internal {
|
||||
|
||||
class BigInteger {
|
||||
public:
|
||||
typedef uint64_t Type;
|
||||
|
||||
BigInteger(const BigInteger& rhs) : count_(rhs.count_) {
|
||||
std::memcpy(digits_, rhs.digits_, count_ * sizeof(Type));
|
||||
}
|
||||
|
||||
explicit BigInteger(uint64_t u) : count_(1) {
|
||||
digits_[0] = u;
|
||||
}
|
||||
|
||||
BigInteger(const char* decimals, size_t length) : count_(1) {
|
||||
RAPIDJSON_ASSERT(length > 0);
|
||||
digits_[0] = 0;
|
||||
size_t i = 0;
|
||||
const size_t kMaxDigitPerIteration = 19; // 2^64 = 18446744073709551616 > 10^19
|
||||
while (length >= kMaxDigitPerIteration) {
|
||||
AppendDecimal64(decimals + i, decimals + i + kMaxDigitPerIteration);
|
||||
length -= kMaxDigitPerIteration;
|
||||
i += kMaxDigitPerIteration;
|
||||
}
|
||||
|
||||
if (length > 0)
|
||||
AppendDecimal64(decimals + i, decimals + i + length);
|
||||
}
|
||||
|
||||
BigInteger& operator=(uint64_t u) {
|
||||
digits_[0] = u;
|
||||
count_ = 1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
BigInteger& operator+=(uint64_t u) {
|
||||
Type backup = digits_[0];
|
||||
digits_[0] += u;
|
||||
for (size_t i = 0; i < count_ - 1; i++) {
|
||||
if (digits_[i] >= backup)
|
||||
return *this; // no carry
|
||||
backup = digits_[i + 1];
|
||||
digits_[i + 1] += 1;
|
||||
}
|
||||
|
||||
// Last carry
|
||||
if (digits_[count_ - 1] < backup)
|
||||
PushBack(1);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
BigInteger& operator*=(uint64_t u) {
|
||||
if (u == 0) return *this = 0;
|
||||
if (u == 1) return *this;
|
||||
if (*this == 1) return *this = u;
|
||||
|
||||
uint64_t k = 0;
|
||||
for (size_t i = 0; i < count_; i++) {
|
||||
uint64_t hi;
|
||||
digits_[i] = MulAdd64(digits_[i], u, k, &hi);
|
||||
k = hi;
|
||||
}
|
||||
|
||||
if (k > 0)
|
||||
PushBack(k);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
BigInteger& operator*=(uint32_t u) {
|
||||
if (u == 0) return *this = 0;
|
||||
if (u == 1) return *this;
|
||||
if (*this == 1) return *this = u;
|
||||
|
||||
uint32_t k = 0;
|
||||
for (size_t i = 0; i < count_; i++) {
|
||||
const uint64_t c = digits_[i] >> 32;
|
||||
const uint64_t d = digits_[i] & 0xFFFFFFFF;
|
||||
const uint64_t uc = u * c;
|
||||
const uint64_t ud = u * d;
|
||||
const uint64_t p0 = ud + k;
|
||||
const uint64_t p1 = uc + (p0 >> 32);
|
||||
digits_[i] = (p0 & 0xFFFFFFFF) | (p1 << 32);
|
||||
k = p1 >> 32;
|
||||
}
|
||||
|
||||
if (k > 0)
|
||||
PushBack(k);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
BigInteger& operator<<=(size_t shift) {
|
||||
if (IsZero() || shift == 0) return *this;
|
||||
|
||||
size_t offset = shift / kTypeBit;
|
||||
size_t interShift = shift % kTypeBit;
|
||||
RAPIDJSON_ASSERT(count_ + offset <= kCapacity);
|
||||
|
||||
if (interShift == 0) {
|
||||
std::memmove(&digits_[count_ - 1 + offset], &digits_[count_ - 1], count_ * sizeof(Type));
|
||||
count_ += offset;
|
||||
}
|
||||
else {
|
||||
digits_[count_] = 0;
|
||||
for (size_t i = count_; i > 0; i--)
|
||||
digits_[i + offset] = (digits_[i] << interShift) | (digits_[i - 1] >> (kTypeBit - interShift));
|
||||
digits_[offset] = digits_[0] << interShift;
|
||||
count_ += offset;
|
||||
if (digits_[count_])
|
||||
count_++;
|
||||
}
|
||||
|
||||
std::memset(digits_, 0, offset * sizeof(Type));
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const BigInteger& rhs) const {
|
||||
return count_ == rhs.count_ && std::memcmp(digits_, rhs.digits_, count_ * sizeof(Type)) == 0;
|
||||
}
|
||||
|
||||
bool operator==(const Type rhs) const {
|
||||
return count_ == 1 && digits_[0] == rhs;
|
||||
}
|
||||
|
||||
BigInteger& MultiplyPow5(unsigned exp) {
|
||||
static const uint32_t kPow5[12] = {
|
||||
5,
|
||||
5 * 5,
|
||||
5 * 5 * 5,
|
||||
5 * 5 * 5 * 5,
|
||||
5 * 5 * 5 * 5 * 5,
|
||||
5 * 5 * 5 * 5 * 5 * 5,
|
||||
5 * 5 * 5 * 5 * 5 * 5 * 5,
|
||||
5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
|
||||
5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
|
||||
5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
|
||||
5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
|
||||
5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5
|
||||
};
|
||||
if (exp == 0) return *this;
|
||||
for (; exp >= 27; exp -= 27) *this *= RAPIDJSON_UINT64_C2(0X6765C793, 0XFA10079D); // 5^27
|
||||
for (; exp >= 13; exp -= 13) *this *= static_cast<uint32_t>(1220703125u); // 5^13
|
||||
if (exp > 0) *this *= kPow5[exp - 1];
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Compute absolute difference of this and rhs.
|
||||
// Assume this != rhs
|
||||
bool Difference(const BigInteger& rhs, BigInteger* out) const {
|
||||
int cmp = Compare(rhs);
|
||||
RAPIDJSON_ASSERT(cmp != 0);
|
||||
const BigInteger *a, *b; // Makes a > b
|
||||
bool ret;
|
||||
if (cmp < 0) { a = &rhs; b = this; ret = true; }
|
||||
else { a = this; b = &rhs; ret = false; }
|
||||
|
||||
Type borrow = 0;
|
||||
for (size_t i = 0; i < a->count_; i++) {
|
||||
Type d = a->digits_[i] - borrow;
|
||||
if (i < b->count_)
|
||||
d -= b->digits_[i];
|
||||
borrow = (d > a->digits_[i]) ? 1 : 0;
|
||||
out->digits_[i] = d;
|
||||
if (d != 0)
|
||||
out->count_ = i + 1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int Compare(const BigInteger& rhs) const {
|
||||
if (count_ != rhs.count_)
|
||||
return count_ < rhs.count_ ? -1 : 1;
|
||||
|
||||
for (size_t i = count_; i-- > 0;)
|
||||
if (digits_[i] != rhs.digits_[i])
|
||||
return digits_[i] < rhs.digits_[i] ? -1 : 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t GetCount() const { return count_; }
|
||||
Type GetDigit(size_t index) const { RAPIDJSON_ASSERT(index < count_); return digits_[index]; }
|
||||
bool IsZero() const { return count_ == 1 && digits_[0] == 0; }
|
||||
|
||||
private:
|
||||
void AppendDecimal64(const char* begin, const char* end) {
|
||||
uint64_t u = ParseUint64(begin, end);
|
||||
if (IsZero())
|
||||
*this = u;
|
||||
else {
|
||||
unsigned exp = static_cast<unsigned>(end - begin);
|
||||
(MultiplyPow5(exp) <<= exp) += u; // *this = *this * 10^exp + u
|
||||
}
|
||||
}
|
||||
|
||||
void PushBack(Type digit) {
|
||||
RAPIDJSON_ASSERT(count_ < kCapacity);
|
||||
digits_[count_++] = digit;
|
||||
}
|
||||
|
||||
static uint64_t ParseUint64(const char* begin, const char* end) {
|
||||
uint64_t r = 0;
|
||||
for (const char* p = begin; p != end; ++p) {
|
||||
RAPIDJSON_ASSERT(*p >= '0' && *p <= '9');
|
||||
r = r * 10 + (*p - '0');
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
// Assume a * b + k < 2^128
|
||||
static uint64_t MulAdd64(uint64_t a, uint64_t b, uint64_t k, uint64_t* outHigh) {
|
||||
#if defined(_MSC_VER) && defined(_M_AMD64)
|
||||
uint64_t low = _umul128(a, b, outHigh) + k;
|
||||
if (low < k)
|
||||
(*outHigh)++;
|
||||
return low;
|
||||
#elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__)
|
||||
__extension__ typedef unsigned __int128 uint128;
|
||||
uint128 p = static_cast<uint128>(a) * static_cast<uint128>(b);
|
||||
p += k;
|
||||
*outHigh = p >> 64;
|
||||
return static_cast<uint64_t>(p);
|
||||
#else
|
||||
const uint64_t a0 = a & 0xFFFFFFFF, a1 = a >> 32, b0 = b & 0xFFFFFFFF, b1 = b >> 32;
|
||||
uint64_t x0 = a0 * b0, x1 = a0 * b1, x2 = a1 * b0, x3 = a1 * b1;
|
||||
x1 += (x0 >> 32); // can't give carry
|
||||
x1 += x2;
|
||||
if (x1 < x2)
|
||||
x3 += (static_cast<uint64_t>(1) << 32);
|
||||
uint64_t lo = (x1 << 32) + (x0 & 0xFFFFFFFF);
|
||||
uint64_t hi = x3 + (x1 >> 32);
|
||||
|
||||
lo += k;
|
||||
if (lo < k)
|
||||
hi++;
|
||||
*outHigh = hi;
|
||||
return lo;
|
||||
#endif
|
||||
}
|
||||
|
||||
static const size_t kBitCount = 3328; // 64bit * 54 > 10^1000
|
||||
static const size_t kCapacity = kBitCount / sizeof(Type);
|
||||
static const size_t kTypeBit = sizeof(Type) * 8;
|
||||
|
||||
Type digits_[kCapacity];
|
||||
size_t count_;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_BIGINTEGER_H_
|
||||
@@ -0,0 +1,247 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
// This is a C++ header-only implementation of Grisu2 algorithm from the publication:
|
||||
// Loitsch, Florian. "Printing floating-point numbers quickly and accurately with
|
||||
// integers." ACM Sigplan Notices 45.6 (2010): 233-243.
|
||||
|
||||
#ifndef RAPIDJSON_DIYFP_H_
|
||||
#define RAPIDJSON_DIYFP_H_
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#include <intrin.h>
|
||||
#if defined(_M_AMD64)
|
||||
#pragma intrinsic(_BitScanReverse64)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
namespace internal {
|
||||
|
||||
#ifdef __GNUC__
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(effc++)
|
||||
#endif
|
||||
|
||||
struct DiyFp {
|
||||
DiyFp() {}
|
||||
|
||||
DiyFp(uint64_t fp, int exp) : f(fp), e(exp) {}
|
||||
|
||||
explicit DiyFp(double d) {
|
||||
union {
|
||||
double d;
|
||||
uint64_t u64;
|
||||
} u = { d };
|
||||
|
||||
int biased_e = (u.u64 & kDpExponentMask) >> kDpSignificandSize;
|
||||
uint64_t significand = (u.u64 & kDpSignificandMask);
|
||||
if (biased_e != 0) {
|
||||
f = significand + kDpHiddenBit;
|
||||
e = biased_e - kDpExponentBias;
|
||||
}
|
||||
else {
|
||||
f = significand;
|
||||
e = kDpMinExponent + 1;
|
||||
}
|
||||
}
|
||||
|
||||
DiyFp operator-(const DiyFp& rhs) const {
|
||||
return DiyFp(f - rhs.f, e);
|
||||
}
|
||||
|
||||
DiyFp operator*(const DiyFp& rhs) const {
|
||||
#if defined(_MSC_VER) && defined(_M_AMD64)
|
||||
uint64_t h;
|
||||
uint64_t l = _umul128(f, rhs.f, &h);
|
||||
if (l & (uint64_t(1) << 63)) // rounding
|
||||
h++;
|
||||
return DiyFp(h, e + rhs.e + 64);
|
||||
#elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__)
|
||||
__extension__ typedef unsigned __int128 uint128;
|
||||
uint128 p = static_cast<uint128>(f) * static_cast<uint128>(rhs.f);
|
||||
uint64_t h = p >> 64;
|
||||
uint64_t l = static_cast<uint64_t>(p);
|
||||
if (l & (uint64_t(1) << 63)) // rounding
|
||||
h++;
|
||||
return DiyFp(h, e + rhs.e + 64);
|
||||
#else
|
||||
const uint64_t M32 = 0xFFFFFFFF;
|
||||
const uint64_t a = f >> 32;
|
||||
const uint64_t b = f & M32;
|
||||
const uint64_t c = rhs.f >> 32;
|
||||
const uint64_t d = rhs.f & M32;
|
||||
const uint64_t ac = a * c;
|
||||
const uint64_t bc = b * c;
|
||||
const uint64_t ad = a * d;
|
||||
const uint64_t bd = b * d;
|
||||
uint64_t tmp = (bd >> 32) + (ad & M32) + (bc & M32);
|
||||
tmp += 1U << 31; /// mult_round
|
||||
return DiyFp(ac + (ad >> 32) + (bc >> 32) + (tmp >> 32), e + rhs.e + 64);
|
||||
#endif
|
||||
}
|
||||
|
||||
DiyFp Normalize() const {
|
||||
#if defined(_MSC_VER) && defined(_M_AMD64)
|
||||
unsigned long index;
|
||||
_BitScanReverse64(&index, f);
|
||||
return DiyFp(f << (63 - index), e - (63 - index));
|
||||
#elif defined(__GNUC__) && __GNUC__ >= 4
|
||||
int s = __builtin_clzll(f);
|
||||
return DiyFp(f << s, e - s);
|
||||
#else
|
||||
DiyFp res = *this;
|
||||
while (!(res.f & (static_cast<uint64_t>(1) << 63))) {
|
||||
res.f <<= 1;
|
||||
res.e--;
|
||||
}
|
||||
return res;
|
||||
#endif
|
||||
}
|
||||
|
||||
DiyFp NormalizeBoundary() const {
|
||||
DiyFp res = *this;
|
||||
while (!(res.f & (kDpHiddenBit << 1))) {
|
||||
res.f <<= 1;
|
||||
res.e--;
|
||||
}
|
||||
res.f <<= (kDiySignificandSize - kDpSignificandSize - 2);
|
||||
res.e = res.e - (kDiySignificandSize - kDpSignificandSize - 2);
|
||||
return res;
|
||||
}
|
||||
|
||||
void NormalizedBoundaries(DiyFp* minus, DiyFp* plus) const {
|
||||
DiyFp pl = DiyFp((f << 1) + 1, e - 1).NormalizeBoundary();
|
||||
DiyFp mi = (f == kDpHiddenBit) ? DiyFp((f << 2) - 1, e - 2) : DiyFp((f << 1) - 1, e - 1);
|
||||
mi.f <<= mi.e - pl.e;
|
||||
mi.e = pl.e;
|
||||
*plus = pl;
|
||||
*minus = mi;
|
||||
}
|
||||
|
||||
double ToDouble() const {
|
||||
union {
|
||||
double d;
|
||||
uint64_t u64;
|
||||
}u;
|
||||
const uint64_t be = (e == kDpDenormalExponent && (f & kDpHiddenBit) == 0) ? 0 :
|
||||
static_cast<uint64_t>(e + kDpExponentBias);
|
||||
u.u64 = (f & kDpSignificandMask) | (be << kDpSignificandSize);
|
||||
return u.d;
|
||||
}
|
||||
|
||||
static const int kDiySignificandSize = 64;
|
||||
static const int kDpSignificandSize = 52;
|
||||
static const int kDpExponentBias = 0x3FF + kDpSignificandSize;
|
||||
static const int kDpMaxExponent = 0x7FF - kDpExponentBias;
|
||||
static const int kDpMinExponent = -kDpExponentBias;
|
||||
static const int kDpDenormalExponent = -kDpExponentBias + 1;
|
||||
static const uint64_t kDpExponentMask = RAPIDJSON_UINT64_C2(0x7FF00000, 0x00000000);
|
||||
static const uint64_t kDpSignificandMask = RAPIDJSON_UINT64_C2(0x000FFFFF, 0xFFFFFFFF);
|
||||
static const uint64_t kDpHiddenBit = RAPIDJSON_UINT64_C2(0x00100000, 0x00000000);
|
||||
|
||||
uint64_t f;
|
||||
int e;
|
||||
};
|
||||
|
||||
inline DiyFp GetCachedPowerByIndex(size_t index) {
|
||||
// 10^-348, 10^-340, ..., 10^340
|
||||
static const uint64_t kCachedPowers_F[] = {
|
||||
RAPIDJSON_UINT64_C2(0xfa8fd5a0, 0x081c0288), RAPIDJSON_UINT64_C2(0xbaaee17f, 0xa23ebf76),
|
||||
RAPIDJSON_UINT64_C2(0x8b16fb20, 0x3055ac76), RAPIDJSON_UINT64_C2(0xcf42894a, 0x5dce35ea),
|
||||
RAPIDJSON_UINT64_C2(0x9a6bb0aa, 0x55653b2d), RAPIDJSON_UINT64_C2(0xe61acf03, 0x3d1a45df),
|
||||
RAPIDJSON_UINT64_C2(0xab70fe17, 0xc79ac6ca), RAPIDJSON_UINT64_C2(0xff77b1fc, 0xbebcdc4f),
|
||||
RAPIDJSON_UINT64_C2(0xbe5691ef, 0x416bd60c), RAPIDJSON_UINT64_C2(0x8dd01fad, 0x907ffc3c),
|
||||
RAPIDJSON_UINT64_C2(0xd3515c28, 0x31559a83), RAPIDJSON_UINT64_C2(0x9d71ac8f, 0xada6c9b5),
|
||||
RAPIDJSON_UINT64_C2(0xea9c2277, 0x23ee8bcb), RAPIDJSON_UINT64_C2(0xaecc4991, 0x4078536d),
|
||||
RAPIDJSON_UINT64_C2(0x823c1279, 0x5db6ce57), RAPIDJSON_UINT64_C2(0xc2109436, 0x4dfb5637),
|
||||
RAPIDJSON_UINT64_C2(0x9096ea6f, 0x3848984f), RAPIDJSON_UINT64_C2(0xd77485cb, 0x25823ac7),
|
||||
RAPIDJSON_UINT64_C2(0xa086cfcd, 0x97bf97f4), RAPIDJSON_UINT64_C2(0xef340a98, 0x172aace5),
|
||||
RAPIDJSON_UINT64_C2(0xb23867fb, 0x2a35b28e), RAPIDJSON_UINT64_C2(0x84c8d4df, 0xd2c63f3b),
|
||||
RAPIDJSON_UINT64_C2(0xc5dd4427, 0x1ad3cdba), RAPIDJSON_UINT64_C2(0x936b9fce, 0xbb25c996),
|
||||
RAPIDJSON_UINT64_C2(0xdbac6c24, 0x7d62a584), RAPIDJSON_UINT64_C2(0xa3ab6658, 0x0d5fdaf6),
|
||||
RAPIDJSON_UINT64_C2(0xf3e2f893, 0xdec3f126), RAPIDJSON_UINT64_C2(0xb5b5ada8, 0xaaff80b8),
|
||||
RAPIDJSON_UINT64_C2(0x87625f05, 0x6c7c4a8b), RAPIDJSON_UINT64_C2(0xc9bcff60, 0x34c13053),
|
||||
RAPIDJSON_UINT64_C2(0x964e858c, 0x91ba2655), RAPIDJSON_UINT64_C2(0xdff97724, 0x70297ebd),
|
||||
RAPIDJSON_UINT64_C2(0xa6dfbd9f, 0xb8e5b88f), RAPIDJSON_UINT64_C2(0xf8a95fcf, 0x88747d94),
|
||||
RAPIDJSON_UINT64_C2(0xb9447093, 0x8fa89bcf), RAPIDJSON_UINT64_C2(0x8a08f0f8, 0xbf0f156b),
|
||||
RAPIDJSON_UINT64_C2(0xcdb02555, 0x653131b6), RAPIDJSON_UINT64_C2(0x993fe2c6, 0xd07b7fac),
|
||||
RAPIDJSON_UINT64_C2(0xe45c10c4, 0x2a2b3b06), RAPIDJSON_UINT64_C2(0xaa242499, 0x697392d3),
|
||||
RAPIDJSON_UINT64_C2(0xfd87b5f2, 0x8300ca0e), RAPIDJSON_UINT64_C2(0xbce50864, 0x92111aeb),
|
||||
RAPIDJSON_UINT64_C2(0x8cbccc09, 0x6f5088cc), RAPIDJSON_UINT64_C2(0xd1b71758, 0xe219652c),
|
||||
RAPIDJSON_UINT64_C2(0x9c400000, 0x00000000), RAPIDJSON_UINT64_C2(0xe8d4a510, 0x00000000),
|
||||
RAPIDJSON_UINT64_C2(0xad78ebc5, 0xac620000), RAPIDJSON_UINT64_C2(0x813f3978, 0xf8940984),
|
||||
RAPIDJSON_UINT64_C2(0xc097ce7b, 0xc90715b3), RAPIDJSON_UINT64_C2(0x8f7e32ce, 0x7bea5c70),
|
||||
RAPIDJSON_UINT64_C2(0xd5d238a4, 0xabe98068), RAPIDJSON_UINT64_C2(0x9f4f2726, 0x179a2245),
|
||||
RAPIDJSON_UINT64_C2(0xed63a231, 0xd4c4fb27), RAPIDJSON_UINT64_C2(0xb0de6538, 0x8cc8ada8),
|
||||
RAPIDJSON_UINT64_C2(0x83c7088e, 0x1aab65db), RAPIDJSON_UINT64_C2(0xc45d1df9, 0x42711d9a),
|
||||
RAPIDJSON_UINT64_C2(0x924d692c, 0xa61be758), RAPIDJSON_UINT64_C2(0xda01ee64, 0x1a708dea),
|
||||
RAPIDJSON_UINT64_C2(0xa26da399, 0x9aef774a), RAPIDJSON_UINT64_C2(0xf209787b, 0xb47d6b85),
|
||||
RAPIDJSON_UINT64_C2(0xb454e4a1, 0x79dd1877), RAPIDJSON_UINT64_C2(0x865b8692, 0x5b9bc5c2),
|
||||
RAPIDJSON_UINT64_C2(0xc83553c5, 0xc8965d3d), RAPIDJSON_UINT64_C2(0x952ab45c, 0xfa97a0b3),
|
||||
RAPIDJSON_UINT64_C2(0xde469fbd, 0x99a05fe3), RAPIDJSON_UINT64_C2(0xa59bc234, 0xdb398c25),
|
||||
RAPIDJSON_UINT64_C2(0xf6c69a72, 0xa3989f5c), RAPIDJSON_UINT64_C2(0xb7dcbf53, 0x54e9bece),
|
||||
RAPIDJSON_UINT64_C2(0x88fcf317, 0xf22241e2), RAPIDJSON_UINT64_C2(0xcc20ce9b, 0xd35c78a5),
|
||||
RAPIDJSON_UINT64_C2(0x98165af3, 0x7b2153df), RAPIDJSON_UINT64_C2(0xe2a0b5dc, 0x971f303a),
|
||||
RAPIDJSON_UINT64_C2(0xa8d9d153, 0x5ce3b396), RAPIDJSON_UINT64_C2(0xfb9b7cd9, 0xa4a7443c),
|
||||
RAPIDJSON_UINT64_C2(0xbb764c4c, 0xa7a44410), RAPIDJSON_UINT64_C2(0x8bab8eef, 0xb6409c1a),
|
||||
RAPIDJSON_UINT64_C2(0xd01fef10, 0xa657842c), RAPIDJSON_UINT64_C2(0x9b10a4e5, 0xe9913129),
|
||||
RAPIDJSON_UINT64_C2(0xe7109bfb, 0xa19c0c9d), RAPIDJSON_UINT64_C2(0xac2820d9, 0x623bf429),
|
||||
RAPIDJSON_UINT64_C2(0x80444b5e, 0x7aa7cf85), RAPIDJSON_UINT64_C2(0xbf21e440, 0x03acdd2d),
|
||||
RAPIDJSON_UINT64_C2(0x8e679c2f, 0x5e44ff8f), RAPIDJSON_UINT64_C2(0xd433179d, 0x9c8cb841),
|
||||
RAPIDJSON_UINT64_C2(0x9e19db92, 0xb4e31ba9), RAPIDJSON_UINT64_C2(0xeb96bf6e, 0xbadf77d9),
|
||||
RAPIDJSON_UINT64_C2(0xaf87023b, 0x9bf0ee6b)
|
||||
};
|
||||
static const int16_t kCachedPowers_E[] = {
|
||||
-1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980,
|
||||
-954, -927, -901, -874, -847, -821, -794, -768, -741, -715,
|
||||
-688, -661, -635, -608, -582, -555, -529, -502, -475, -449,
|
||||
-422, -396, -369, -343, -316, -289, -263, -236, -210, -183,
|
||||
-157, -130, -103, -77, -50, -24, 3, 30, 56, 83,
|
||||
109, 136, 162, 189, 216, 242, 269, 295, 322, 348,
|
||||
375, 402, 428, 455, 481, 508, 534, 561, 588, 614,
|
||||
641, 667, 694, 720, 747, 774, 800, 827, 853, 880,
|
||||
907, 933, 960, 986, 1013, 1039, 1066
|
||||
};
|
||||
return DiyFp(kCachedPowers_F[index], kCachedPowers_E[index]);
|
||||
}
|
||||
|
||||
inline DiyFp GetCachedPower(int e, int* K) {
|
||||
|
||||
//int k = static_cast<int>(ceil((-61 - e) * 0.30102999566398114)) + 374;
|
||||
double dk = (-61 - e) * 0.30102999566398114 + 347; // dk must be positive, so can do ceiling in positive
|
||||
int k = static_cast<int>(dk);
|
||||
if (dk - k > 0.0)
|
||||
k++;
|
||||
|
||||
unsigned index = static_cast<unsigned>((k >> 3) + 1);
|
||||
*K = -(-348 + static_cast<int>(index << 3)); // decimal exponent no need lookup table
|
||||
|
||||
return GetCachedPowerByIndex(index);
|
||||
}
|
||||
|
||||
inline DiyFp GetCachedPower10(int exp, int *outExp) {
|
||||
unsigned index = (exp + 348) / 8;
|
||||
*outExp = -348 + index * 8;
|
||||
return GetCachedPowerByIndex(index);
|
||||
}
|
||||
|
||||
#ifdef __GNUC__
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
} // namespace internal
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_DIYFP_H_
|
||||
@@ -0,0 +1,217 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
// This is a C++ header-only implementation of Grisu2 algorithm from the publication:
|
||||
// Loitsch, Florian. "Printing floating-point numbers quickly and accurately with
|
||||
// integers." ACM Sigplan Notices 45.6 (2010): 233-243.
|
||||
|
||||
#ifndef RAPIDJSON_DTOA_
|
||||
#define RAPIDJSON_DTOA_
|
||||
|
||||
#include "itoa.h" // GetDigitsLut()
|
||||
#include "diyfp.h"
|
||||
#include "ieee754.h"
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
namespace internal {
|
||||
|
||||
#ifdef __GNUC__
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(effc++)
|
||||
#endif
|
||||
|
||||
inline void GrisuRound(char* buffer, int len, uint64_t delta, uint64_t rest, uint64_t ten_kappa, uint64_t wp_w) {
|
||||
while (rest < wp_w && delta - rest >= ten_kappa &&
|
||||
(rest + ten_kappa < wp_w || /// closer
|
||||
wp_w - rest > rest + ten_kappa - wp_w)) {
|
||||
buffer[len - 1]--;
|
||||
rest += ten_kappa;
|
||||
}
|
||||
}
|
||||
|
||||
inline unsigned CountDecimalDigit32(uint32_t n) {
|
||||
// Simple pure C++ implementation was faster than __builtin_clz version in this situation.
|
||||
if (n < 10) return 1;
|
||||
if (n < 100) return 2;
|
||||
if (n < 1000) return 3;
|
||||
if (n < 10000) return 4;
|
||||
if (n < 100000) return 5;
|
||||
if (n < 1000000) return 6;
|
||||
if (n < 10000000) return 7;
|
||||
if (n < 100000000) return 8;
|
||||
// Will not reach 10 digits in DigitGen()
|
||||
//if (n < 1000000000) return 9;
|
||||
//return 10;
|
||||
return 9;
|
||||
}
|
||||
|
||||
inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buffer, int* len, int* K) {
|
||||
static const uint32_t kPow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 };
|
||||
const DiyFp one(uint64_t(1) << -Mp.e, Mp.e);
|
||||
const DiyFp wp_w = Mp - W;
|
||||
uint32_t p1 = static_cast<uint32_t>(Mp.f >> -one.e);
|
||||
uint64_t p2 = Mp.f & (one.f - 1);
|
||||
int kappa = CountDecimalDigit32(p1); // kappa in [0, 9]
|
||||
*len = 0;
|
||||
|
||||
while (kappa > 0) {
|
||||
uint32_t d = 0;
|
||||
switch (kappa) {
|
||||
case 9: d = p1 / 100000000; p1 %= 100000000; break;
|
||||
case 8: d = p1 / 10000000; p1 %= 10000000; break;
|
||||
case 7: d = p1 / 1000000; p1 %= 1000000; break;
|
||||
case 6: d = p1 / 100000; p1 %= 100000; break;
|
||||
case 5: d = p1 / 10000; p1 %= 10000; break;
|
||||
case 4: d = p1 / 1000; p1 %= 1000; break;
|
||||
case 3: d = p1 / 100; p1 %= 100; break;
|
||||
case 2: d = p1 / 10; p1 %= 10; break;
|
||||
case 1: d = p1; p1 = 0; break;
|
||||
default:;
|
||||
}
|
||||
if (d || *len)
|
||||
buffer[(*len)++] = static_cast<char>('0' + static_cast<char>(d));
|
||||
kappa--;
|
||||
uint64_t tmp = (static_cast<uint64_t>(p1) << -one.e) + p2;
|
||||
if (tmp <= delta) {
|
||||
*K += kappa;
|
||||
GrisuRound(buffer, *len, delta, tmp, static_cast<uint64_t>(kPow10[kappa]) << -one.e, wp_w.f);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// kappa = 0
|
||||
for (;;) {
|
||||
p2 *= 10;
|
||||
delta *= 10;
|
||||
char d = static_cast<char>(p2 >> -one.e);
|
||||
if (d || *len)
|
||||
buffer[(*len)++] = static_cast<char>('0' + d);
|
||||
p2 &= one.f - 1;
|
||||
kappa--;
|
||||
if (p2 < delta) {
|
||||
*K += kappa;
|
||||
GrisuRound(buffer, *len, delta, p2, one.f, wp_w.f * kPow10[-kappa]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void Grisu2(double value, char* buffer, int* length, int* K) {
|
||||
const DiyFp v(value);
|
||||
DiyFp w_m, w_p;
|
||||
v.NormalizedBoundaries(&w_m, &w_p);
|
||||
|
||||
const DiyFp c_mk = GetCachedPower(w_p.e, K);
|
||||
const DiyFp W = v.Normalize() * c_mk;
|
||||
DiyFp Wp = w_p * c_mk;
|
||||
DiyFp Wm = w_m * c_mk;
|
||||
Wm.f++;
|
||||
Wp.f--;
|
||||
DigitGen(W, Wp, Wp.f - Wm.f, buffer, length, K);
|
||||
}
|
||||
|
||||
inline char* WriteExponent(int K, char* buffer) {
|
||||
if (K < 0) {
|
||||
*buffer++ = '-';
|
||||
K = -K;
|
||||
}
|
||||
|
||||
if (K >= 100) {
|
||||
*buffer++ = static_cast<char>('0' + static_cast<char>(K / 100));
|
||||
K %= 100;
|
||||
const char* d = GetDigitsLut() + K * 2;
|
||||
*buffer++ = d[0];
|
||||
*buffer++ = d[1];
|
||||
}
|
||||
else if (K >= 10) {
|
||||
const char* d = GetDigitsLut() + K * 2;
|
||||
*buffer++ = d[0];
|
||||
*buffer++ = d[1];
|
||||
}
|
||||
else
|
||||
*buffer++ = static_cast<char>('0' + static_cast<char>(K));
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
inline char* Prettify(char* buffer, int length, int k) {
|
||||
const int kk = length + k; // 10^(kk-1) <= v < 10^kk
|
||||
|
||||
if (length <= kk && kk <= 21) {
|
||||
// 1234e7 -> 12340000000
|
||||
for (int i = length; i < kk; i++)
|
||||
buffer[i] = '0';
|
||||
buffer[kk] = '.';
|
||||
buffer[kk + 1] = '0';
|
||||
return &buffer[kk + 2];
|
||||
}
|
||||
else if (0 < kk && kk <= 21) {
|
||||
// 1234e-2 -> 12.34
|
||||
std::memmove(&buffer[kk + 1], &buffer[kk], length - kk);
|
||||
buffer[kk] = '.';
|
||||
return &buffer[length + 1];
|
||||
}
|
||||
else if (-6 < kk && kk <= 0) {
|
||||
// 1234e-6 -> 0.001234
|
||||
const int offset = 2 - kk;
|
||||
std::memmove(&buffer[offset], &buffer[0], length);
|
||||
buffer[0] = '0';
|
||||
buffer[1] = '.';
|
||||
for (int i = 2; i < offset; i++)
|
||||
buffer[i] = '0';
|
||||
return &buffer[length + offset];
|
||||
}
|
||||
else if (length == 1) {
|
||||
// 1e30
|
||||
buffer[1] = 'e';
|
||||
return WriteExponent(kk - 1, &buffer[2]);
|
||||
}
|
||||
else {
|
||||
// 1234e30 -> 1.234e33
|
||||
std::memmove(&buffer[2], &buffer[1], length - 1);
|
||||
buffer[1] = '.';
|
||||
buffer[length + 1] = 'e';
|
||||
return WriteExponent(kk - 1, &buffer[0 + length + 2]);
|
||||
}
|
||||
}
|
||||
|
||||
inline char* dtoa(double value, char* buffer) {
|
||||
Double d(value);
|
||||
if (d.IsZero()) {
|
||||
if (d.Sign())
|
||||
*buffer++ = '-'; // -0.0, Issue #289
|
||||
buffer[0] = '0';
|
||||
buffer[1] = '.';
|
||||
buffer[2] = '0';
|
||||
return &buffer[3];
|
||||
}
|
||||
else {
|
||||
if (value < 0) {
|
||||
*buffer++ = '-';
|
||||
value = -value;
|
||||
}
|
||||
int length, K;
|
||||
Grisu2(value, buffer, &length, &K);
|
||||
return Prettify(buffer, length, K);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __GNUC__
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
} // namespace internal
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_DTOA_
|
||||
@@ -0,0 +1,77 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_IEEE754_
|
||||
#define RAPIDJSON_IEEE754_
|
||||
|
||||
#include "../rapidjson.h"
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
namespace internal {
|
||||
|
||||
class Double {
|
||||
public:
|
||||
Double() {}
|
||||
Double(double d) : d(d) {}
|
||||
Double(uint64_t u) : u(u) {}
|
||||
|
||||
double Value() const { return d; }
|
||||
uint64_t Uint64Value() const { return u; }
|
||||
|
||||
double NextPositiveDouble() const {
|
||||
RAPIDJSON_ASSERT(!Sign());
|
||||
return Double(u + 1).Value();
|
||||
}
|
||||
|
||||
bool Sign() const { return (u & kSignMask) != 0; }
|
||||
uint64_t Significand() const { return u & kSignificandMask; }
|
||||
int Exponent() const { return ((u & kExponentMask) >> kSignificandSize) - kExponentBias; }
|
||||
|
||||
bool IsNan() const { return (u & kExponentMask) == kExponentMask && Significand() != 0; }
|
||||
bool IsInf() const { return (u & kExponentMask) == kExponentMask && Significand() == 0; }
|
||||
bool IsNormal() const { return (u & kExponentMask) != 0 || Significand() == 0; }
|
||||
bool IsZero() const { return (u & (kExponentMask | kSignificandMask)) == 0; }
|
||||
|
||||
uint64_t IntegerSignificand() const { return IsNormal() ? Significand() | kHiddenBit : Significand(); }
|
||||
int IntegerExponent() const { return (IsNormal() ? Exponent() : kDenormalExponent) - kSignificandSize; }
|
||||
uint64_t ToBias() const { return (u & kSignMask) ? ~u + 1 : u | kSignMask; }
|
||||
|
||||
static unsigned EffectiveSignificandSize(int order) {
|
||||
if (order >= -1021)
|
||||
return 53;
|
||||
else if (order <= -1074)
|
||||
return 0;
|
||||
else
|
||||
return order + 1074;
|
||||
}
|
||||
|
||||
private:
|
||||
static const int kSignificandSize = 52;
|
||||
static const int kExponentBias = 0x3FF;
|
||||
static const int kDenormalExponent = 1 - kExponentBias;
|
||||
static const uint64_t kSignMask = RAPIDJSON_UINT64_C2(0x80000000, 0x00000000);
|
||||
static const uint64_t kExponentMask = RAPIDJSON_UINT64_C2(0x7FF00000, 0x00000000);
|
||||
static const uint64_t kSignificandMask = RAPIDJSON_UINT64_C2(0x000FFFFF, 0xFFFFFFFF);
|
||||
static const uint64_t kHiddenBit = RAPIDJSON_UINT64_C2(0x00100000, 0x00000000);
|
||||
|
||||
union {
|
||||
double d;
|
||||
uint64_t u;
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_IEEE754_
|
||||
@@ -0,0 +1,304 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_ITOA_
|
||||
#define RAPIDJSON_ITOA_
|
||||
|
||||
#include "../rapidjson.h"
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
namespace internal {
|
||||
|
||||
inline const char* GetDigitsLut() {
|
||||
static const char cDigitsLut[200] = {
|
||||
'0','0','0','1','0','2','0','3','0','4','0','5','0','6','0','7','0','8','0','9',
|
||||
'1','0','1','1','1','2','1','3','1','4','1','5','1','6','1','7','1','8','1','9',
|
||||
'2','0','2','1','2','2','2','3','2','4','2','5','2','6','2','7','2','8','2','9',
|
||||
'3','0','3','1','3','2','3','3','3','4','3','5','3','6','3','7','3','8','3','9',
|
||||
'4','0','4','1','4','2','4','3','4','4','4','5','4','6','4','7','4','8','4','9',
|
||||
'5','0','5','1','5','2','5','3','5','4','5','5','5','6','5','7','5','8','5','9',
|
||||
'6','0','6','1','6','2','6','3','6','4','6','5','6','6','6','7','6','8','6','9',
|
||||
'7','0','7','1','7','2','7','3','7','4','7','5','7','6','7','7','7','8','7','9',
|
||||
'8','0','8','1','8','2','8','3','8','4','8','5','8','6','8','7','8','8','8','9',
|
||||
'9','0','9','1','9','2','9','3','9','4','9','5','9','6','9','7','9','8','9','9'
|
||||
};
|
||||
return cDigitsLut;
|
||||
}
|
||||
|
||||
inline char* u32toa(uint32_t value, char* buffer) {
|
||||
const char* cDigitsLut = GetDigitsLut();
|
||||
|
||||
if (value < 10000) {
|
||||
const uint32_t d1 = (value / 100) << 1;
|
||||
const uint32_t d2 = (value % 100) << 1;
|
||||
|
||||
if (value >= 1000)
|
||||
*buffer++ = cDigitsLut[d1];
|
||||
if (value >= 100)
|
||||
*buffer++ = cDigitsLut[d1 + 1];
|
||||
if (value >= 10)
|
||||
*buffer++ = cDigitsLut[d2];
|
||||
*buffer++ = cDigitsLut[d2 + 1];
|
||||
}
|
||||
else if (value < 100000000) {
|
||||
// value = bbbbcccc
|
||||
const uint32_t b = value / 10000;
|
||||
const uint32_t c = value % 10000;
|
||||
|
||||
const uint32_t d1 = (b / 100) << 1;
|
||||
const uint32_t d2 = (b % 100) << 1;
|
||||
|
||||
const uint32_t d3 = (c / 100) << 1;
|
||||
const uint32_t d4 = (c % 100) << 1;
|
||||
|
||||
if (value >= 10000000)
|
||||
*buffer++ = cDigitsLut[d1];
|
||||
if (value >= 1000000)
|
||||
*buffer++ = cDigitsLut[d1 + 1];
|
||||
if (value >= 100000)
|
||||
*buffer++ = cDigitsLut[d2];
|
||||
*buffer++ = cDigitsLut[d2 + 1];
|
||||
|
||||
*buffer++ = cDigitsLut[d3];
|
||||
*buffer++ = cDigitsLut[d3 + 1];
|
||||
*buffer++ = cDigitsLut[d4];
|
||||
*buffer++ = cDigitsLut[d4 + 1];
|
||||
}
|
||||
else {
|
||||
// value = aabbbbcccc in decimal
|
||||
|
||||
const uint32_t a = value / 100000000; // 1 to 42
|
||||
value %= 100000000;
|
||||
|
||||
if (a >= 10) {
|
||||
const unsigned i = a << 1;
|
||||
*buffer++ = cDigitsLut[i];
|
||||
*buffer++ = cDigitsLut[i + 1];
|
||||
}
|
||||
else
|
||||
*buffer++ = static_cast<char>('0' + static_cast<char>(a));
|
||||
|
||||
const uint32_t b = value / 10000; // 0 to 9999
|
||||
const uint32_t c = value % 10000; // 0 to 9999
|
||||
|
||||
const uint32_t d1 = (b / 100) << 1;
|
||||
const uint32_t d2 = (b % 100) << 1;
|
||||
|
||||
const uint32_t d3 = (c / 100) << 1;
|
||||
const uint32_t d4 = (c % 100) << 1;
|
||||
|
||||
*buffer++ = cDigitsLut[d1];
|
||||
*buffer++ = cDigitsLut[d1 + 1];
|
||||
*buffer++ = cDigitsLut[d2];
|
||||
*buffer++ = cDigitsLut[d2 + 1];
|
||||
*buffer++ = cDigitsLut[d3];
|
||||
*buffer++ = cDigitsLut[d3 + 1];
|
||||
*buffer++ = cDigitsLut[d4];
|
||||
*buffer++ = cDigitsLut[d4 + 1];
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
inline char* i32toa(int32_t value, char* buffer) {
|
||||
uint32_t u = static_cast<uint32_t>(value);
|
||||
if (value < 0) {
|
||||
*buffer++ = '-';
|
||||
u = ~u + 1;
|
||||
}
|
||||
|
||||
return u32toa(u, buffer);
|
||||
}
|
||||
|
||||
inline char* u64toa(uint64_t value, char* buffer) {
|
||||
const char* cDigitsLut = GetDigitsLut();
|
||||
const uint64_t kTen8 = 100000000;
|
||||
const uint64_t kTen9 = kTen8 * 10;
|
||||
const uint64_t kTen10 = kTen8 * 100;
|
||||
const uint64_t kTen11 = kTen8 * 1000;
|
||||
const uint64_t kTen12 = kTen8 * 10000;
|
||||
const uint64_t kTen13 = kTen8 * 100000;
|
||||
const uint64_t kTen14 = kTen8 * 1000000;
|
||||
const uint64_t kTen15 = kTen8 * 10000000;
|
||||
const uint64_t kTen16 = kTen8 * kTen8;
|
||||
|
||||
if (value < kTen8) {
|
||||
uint32_t v = static_cast<uint32_t>(value);
|
||||
if (v < 10000) {
|
||||
const uint32_t d1 = (v / 100) << 1;
|
||||
const uint32_t d2 = (v % 100) << 1;
|
||||
|
||||
if (v >= 1000)
|
||||
*buffer++ = cDigitsLut[d1];
|
||||
if (v >= 100)
|
||||
*buffer++ = cDigitsLut[d1 + 1];
|
||||
if (v >= 10)
|
||||
*buffer++ = cDigitsLut[d2];
|
||||
*buffer++ = cDigitsLut[d2 + 1];
|
||||
}
|
||||
else {
|
||||
// value = bbbbcccc
|
||||
const uint32_t b = v / 10000;
|
||||
const uint32_t c = v % 10000;
|
||||
|
||||
const uint32_t d1 = (b / 100) << 1;
|
||||
const uint32_t d2 = (b % 100) << 1;
|
||||
|
||||
const uint32_t d3 = (c / 100) << 1;
|
||||
const uint32_t d4 = (c % 100) << 1;
|
||||
|
||||
if (value >= 10000000)
|
||||
*buffer++ = cDigitsLut[d1];
|
||||
if (value >= 1000000)
|
||||
*buffer++ = cDigitsLut[d1 + 1];
|
||||
if (value >= 100000)
|
||||
*buffer++ = cDigitsLut[d2];
|
||||
*buffer++ = cDigitsLut[d2 + 1];
|
||||
|
||||
*buffer++ = cDigitsLut[d3];
|
||||
*buffer++ = cDigitsLut[d3 + 1];
|
||||
*buffer++ = cDigitsLut[d4];
|
||||
*buffer++ = cDigitsLut[d4 + 1];
|
||||
}
|
||||
}
|
||||
else if (value < kTen16) {
|
||||
const uint32_t v0 = static_cast<uint32_t>(value / kTen8);
|
||||
const uint32_t v1 = static_cast<uint32_t>(value % kTen8);
|
||||
|
||||
const uint32_t b0 = v0 / 10000;
|
||||
const uint32_t c0 = v0 % 10000;
|
||||
|
||||
const uint32_t d1 = (b0 / 100) << 1;
|
||||
const uint32_t d2 = (b0 % 100) << 1;
|
||||
|
||||
const uint32_t d3 = (c0 / 100) << 1;
|
||||
const uint32_t d4 = (c0 % 100) << 1;
|
||||
|
||||
const uint32_t b1 = v1 / 10000;
|
||||
const uint32_t c1 = v1 % 10000;
|
||||
|
||||
const uint32_t d5 = (b1 / 100) << 1;
|
||||
const uint32_t d6 = (b1 % 100) << 1;
|
||||
|
||||
const uint32_t d7 = (c1 / 100) << 1;
|
||||
const uint32_t d8 = (c1 % 100) << 1;
|
||||
|
||||
if (value >= kTen15)
|
||||
*buffer++ = cDigitsLut[d1];
|
||||
if (value >= kTen14)
|
||||
*buffer++ = cDigitsLut[d1 + 1];
|
||||
if (value >= kTen13)
|
||||
*buffer++ = cDigitsLut[d2];
|
||||
if (value >= kTen12)
|
||||
*buffer++ = cDigitsLut[d2 + 1];
|
||||
if (value >= kTen11)
|
||||
*buffer++ = cDigitsLut[d3];
|
||||
if (value >= kTen10)
|
||||
*buffer++ = cDigitsLut[d3 + 1];
|
||||
if (value >= kTen9)
|
||||
*buffer++ = cDigitsLut[d4];
|
||||
if (value >= kTen8)
|
||||
*buffer++ = cDigitsLut[d4 + 1];
|
||||
|
||||
*buffer++ = cDigitsLut[d5];
|
||||
*buffer++ = cDigitsLut[d5 + 1];
|
||||
*buffer++ = cDigitsLut[d6];
|
||||
*buffer++ = cDigitsLut[d6 + 1];
|
||||
*buffer++ = cDigitsLut[d7];
|
||||
*buffer++ = cDigitsLut[d7 + 1];
|
||||
*buffer++ = cDigitsLut[d8];
|
||||
*buffer++ = cDigitsLut[d8 + 1];
|
||||
}
|
||||
else {
|
||||
const uint32_t a = static_cast<uint32_t>(value / kTen16); // 1 to 1844
|
||||
value %= kTen16;
|
||||
|
||||
if (a < 10)
|
||||
*buffer++ = static_cast<char>('0' + static_cast<char>(a));
|
||||
else if (a < 100) {
|
||||
const uint32_t i = a << 1;
|
||||
*buffer++ = cDigitsLut[i];
|
||||
*buffer++ = cDigitsLut[i + 1];
|
||||
}
|
||||
else if (a < 1000) {
|
||||
*buffer++ = static_cast<char>('0' + static_cast<char>(a / 100));
|
||||
|
||||
const uint32_t i = (a % 100) << 1;
|
||||
*buffer++ = cDigitsLut[i];
|
||||
*buffer++ = cDigitsLut[i + 1];
|
||||
}
|
||||
else {
|
||||
const uint32_t i = (a / 100) << 1;
|
||||
const uint32_t j = (a % 100) << 1;
|
||||
*buffer++ = cDigitsLut[i];
|
||||
*buffer++ = cDigitsLut[i + 1];
|
||||
*buffer++ = cDigitsLut[j];
|
||||
*buffer++ = cDigitsLut[j + 1];
|
||||
}
|
||||
|
||||
const uint32_t v0 = static_cast<uint32_t>(value / kTen8);
|
||||
const uint32_t v1 = static_cast<uint32_t>(value % kTen8);
|
||||
|
||||
const uint32_t b0 = v0 / 10000;
|
||||
const uint32_t c0 = v0 % 10000;
|
||||
|
||||
const uint32_t d1 = (b0 / 100) << 1;
|
||||
const uint32_t d2 = (b0 % 100) << 1;
|
||||
|
||||
const uint32_t d3 = (c0 / 100) << 1;
|
||||
const uint32_t d4 = (c0 % 100) << 1;
|
||||
|
||||
const uint32_t b1 = v1 / 10000;
|
||||
const uint32_t c1 = v1 % 10000;
|
||||
|
||||
const uint32_t d5 = (b1 / 100) << 1;
|
||||
const uint32_t d6 = (b1 % 100) << 1;
|
||||
|
||||
const uint32_t d7 = (c1 / 100) << 1;
|
||||
const uint32_t d8 = (c1 % 100) << 1;
|
||||
|
||||
*buffer++ = cDigitsLut[d1];
|
||||
*buffer++ = cDigitsLut[d1 + 1];
|
||||
*buffer++ = cDigitsLut[d2];
|
||||
*buffer++ = cDigitsLut[d2 + 1];
|
||||
*buffer++ = cDigitsLut[d3];
|
||||
*buffer++ = cDigitsLut[d3 + 1];
|
||||
*buffer++ = cDigitsLut[d4];
|
||||
*buffer++ = cDigitsLut[d4 + 1];
|
||||
*buffer++ = cDigitsLut[d5];
|
||||
*buffer++ = cDigitsLut[d5 + 1];
|
||||
*buffer++ = cDigitsLut[d6];
|
||||
*buffer++ = cDigitsLut[d6 + 1];
|
||||
*buffer++ = cDigitsLut[d7];
|
||||
*buffer++ = cDigitsLut[d7 + 1];
|
||||
*buffer++ = cDigitsLut[d8];
|
||||
*buffer++ = cDigitsLut[d8 + 1];
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
inline char* i64toa(int64_t value, char* buffer) {
|
||||
uint64_t u = static_cast<uint64_t>(value);
|
||||
if (value < 0) {
|
||||
*buffer++ = '-';
|
||||
u = ~u + 1;
|
||||
}
|
||||
|
||||
return u64toa(u, buffer);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_ITOA_
|
||||
@@ -0,0 +1,183 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_INTERNAL_META_H_
|
||||
#define RAPIDJSON_INTERNAL_META_H_
|
||||
|
||||
#ifndef RAPIDJSON_RAPIDJSON_H_
|
||||
#error <rapidjson.h> not yet included. Do not include this file directly.
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(effc++)
|
||||
#endif
|
||||
#if defined(_MSC_VER)
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(6334)
|
||||
#endif
|
||||
|
||||
#if RAPIDJSON_HAS_CXX11_TYPETRAITS
|
||||
#include <type_traits>
|
||||
#endif
|
||||
|
||||
//@cond RAPIDJSON_INTERNAL
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
namespace internal {
|
||||
|
||||
// Helper to wrap/convert arbitrary types to void, useful for arbitrary type matching
|
||||
template <typename T> struct Void { typedef void Type; };
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// BoolType, TrueType, FalseType
|
||||
//
|
||||
template <bool Cond> struct BoolType {
|
||||
static const bool Value = Cond;
|
||||
typedef BoolType Type;
|
||||
};
|
||||
typedef BoolType<true> TrueType;
|
||||
typedef BoolType<false> FalseType;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// SelectIf, BoolExpr, NotExpr, AndExpr, OrExpr
|
||||
//
|
||||
|
||||
template <bool C> struct SelectIfImpl { template <typename T1, typename T2> struct Apply { typedef T1 Type; }; };
|
||||
template <> struct SelectIfImpl<false> { template <typename T1, typename T2> struct Apply { typedef T2 Type; }; };
|
||||
template <bool C, typename T1, typename T2> struct SelectIfCond : SelectIfImpl<C>::template Apply<T1,T2> {};
|
||||
template <typename C, typename T1, typename T2> struct SelectIf : SelectIfCond<C::Value, T1, T2> {};
|
||||
|
||||
template <bool Cond1, bool Cond2> struct AndExprCond : FalseType {};
|
||||
template <> struct AndExprCond<true, true> : TrueType {};
|
||||
template <bool Cond1, bool Cond2> struct OrExprCond : TrueType {};
|
||||
template <> struct OrExprCond<false, false> : FalseType {};
|
||||
|
||||
template <typename C> struct BoolExpr : SelectIf<C,TrueType,FalseType>::Type {};
|
||||
template <typename C> struct NotExpr : SelectIf<C,FalseType,TrueType>::Type {};
|
||||
template <typename C1, typename C2> struct AndExpr : AndExprCond<C1::Value, C2::Value>::Type {};
|
||||
template <typename C1, typename C2> struct OrExpr : OrExprCond<C1::Value, C2::Value>::Type {};
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// AddConst, MaybeAddConst, RemoveConst
|
||||
template <typename T> struct AddConst { typedef const T Type; };
|
||||
template <bool Constify, typename T> struct MaybeAddConst : SelectIfCond<Constify, const T, T> {};
|
||||
template <typename T> struct RemoveConst { typedef T Type; };
|
||||
template <typename T> struct RemoveConst<const T> { typedef T Type; };
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// IsSame, IsConst, IsMoreConst, IsPointer
|
||||
//
|
||||
template <typename T, typename U> struct IsSame : FalseType {};
|
||||
template <typename T> struct IsSame<T, T> : TrueType {};
|
||||
|
||||
template <typename T> struct IsConst : FalseType {};
|
||||
template <typename T> struct IsConst<const T> : TrueType {};
|
||||
|
||||
template <typename CT, typename T>
|
||||
struct IsMoreConst
|
||||
: AndExpr<IsSame<typename RemoveConst<CT>::Type, typename RemoveConst<T>::Type>,
|
||||
BoolType<IsConst<CT>::Value >= IsConst<T>::Value> >::Type {};
|
||||
|
||||
template <typename T> struct IsPointer : FalseType {};
|
||||
template <typename T> struct IsPointer<T*> : TrueType {};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// IsBaseOf
|
||||
//
|
||||
#if RAPIDJSON_HAS_CXX11_TYPETRAITS
|
||||
|
||||
template <typename B, typename D> struct IsBaseOf
|
||||
: BoolType< ::std::is_base_of<B,D>::value> {};
|
||||
|
||||
#else // simplified version adopted from Boost
|
||||
|
||||
template<typename B, typename D> struct IsBaseOfImpl {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(B) != 0);
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(D) != 0);
|
||||
|
||||
typedef char (&Yes)[1];
|
||||
typedef char (&No) [2];
|
||||
|
||||
template <typename T>
|
||||
static Yes Check(const D*, T);
|
||||
static No Check(const B*, int);
|
||||
|
||||
struct Host {
|
||||
operator const B*() const;
|
||||
operator const D*();
|
||||
};
|
||||
|
||||
enum { Value = (sizeof(Check(Host(), 0)) == sizeof(Yes)) };
|
||||
};
|
||||
|
||||
template <typename B, typename D> struct IsBaseOf
|
||||
: OrExpr<IsSame<B, D>, BoolExpr<IsBaseOfImpl<B, D> > >::Type {};
|
||||
|
||||
#endif // RAPIDJSON_HAS_CXX11_TYPETRAITS
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// EnableIf / DisableIf
|
||||
//
|
||||
template <bool Condition, typename T = void> struct EnableIfCond { typedef T Type; };
|
||||
template <typename T> struct EnableIfCond<false, T> { /* empty */ };
|
||||
|
||||
template <bool Condition, typename T = void> struct DisableIfCond { typedef T Type; };
|
||||
template <typename T> struct DisableIfCond<true, T> { /* empty */ };
|
||||
|
||||
template <typename Condition, typename T = void>
|
||||
struct EnableIf : EnableIfCond<Condition::Value, T> {};
|
||||
|
||||
template <typename Condition, typename T = void>
|
||||
struct DisableIf : DisableIfCond<Condition::Value, T> {};
|
||||
|
||||
// SFINAE helpers
|
||||
struct SfinaeTag {};
|
||||
template <typename T> struct RemoveSfinaeTag;
|
||||
template <typename T> struct RemoveSfinaeTag<SfinaeTag&(*)(T)> { typedef T Type; };
|
||||
|
||||
#define RAPIDJSON_REMOVEFPTR_(type) \
|
||||
typename ::RAPIDJSON_NAMESPACE::internal::RemoveSfinaeTag \
|
||||
< ::RAPIDJSON_NAMESPACE::internal::SfinaeTag&(*) type>::Type
|
||||
|
||||
#define RAPIDJSON_ENABLEIF(cond) \
|
||||
typename ::RAPIDJSON_NAMESPACE::internal::EnableIf \
|
||||
<RAPIDJSON_REMOVEFPTR_(cond)>::Type * = NULL
|
||||
|
||||
#define RAPIDJSON_DISABLEIF(cond) \
|
||||
typename ::RAPIDJSON_NAMESPACE::internal::DisableIf \
|
||||
<RAPIDJSON_REMOVEFPTR_(cond)>::Type * = NULL
|
||||
|
||||
#define RAPIDJSON_ENABLEIF_RETURN(cond,returntype) \
|
||||
typename ::RAPIDJSON_NAMESPACE::internal::EnableIf \
|
||||
<RAPIDJSON_REMOVEFPTR_(cond), \
|
||||
RAPIDJSON_REMOVEFPTR_(returntype)>::Type
|
||||
|
||||
#define RAPIDJSON_DISABLEIF_RETURN(cond,returntype) \
|
||||
typename ::RAPIDJSON_NAMESPACE::internal::DisableIf \
|
||||
<RAPIDJSON_REMOVEFPTR_(cond), \
|
||||
RAPIDJSON_REMOVEFPTR_(returntype)>::Type
|
||||
|
||||
} // namespace internal
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
//@endcond
|
||||
|
||||
#if defined(__GNUC__) || defined(_MSC_VER)
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
#endif // RAPIDJSON_INTERNAL_META_H_
|
||||
@@ -0,0 +1,53 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_POW10_
|
||||
#define RAPIDJSON_POW10_
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
namespace internal {
|
||||
|
||||
//! Computes integer powers of 10 in double (10.0^n).
|
||||
/*! This function uses lookup table for fast and accurate results.
|
||||
\param n non-negative exponent. Must <= 308.
|
||||
\return 10.0^n
|
||||
*/
|
||||
inline double Pow10(int n) {
|
||||
static const double e[] = { // 1e-0...1e308: 309 * 8 bytes = 2472 bytes
|
||||
1e+0,
|
||||
1e+1, 1e+2, 1e+3, 1e+4, 1e+5, 1e+6, 1e+7, 1e+8, 1e+9, 1e+10, 1e+11, 1e+12, 1e+13, 1e+14, 1e+15, 1e+16, 1e+17, 1e+18, 1e+19, 1e+20,
|
||||
1e+21, 1e+22, 1e+23, 1e+24, 1e+25, 1e+26, 1e+27, 1e+28, 1e+29, 1e+30, 1e+31, 1e+32, 1e+33, 1e+34, 1e+35, 1e+36, 1e+37, 1e+38, 1e+39, 1e+40,
|
||||
1e+41, 1e+42, 1e+43, 1e+44, 1e+45, 1e+46, 1e+47, 1e+48, 1e+49, 1e+50, 1e+51, 1e+52, 1e+53, 1e+54, 1e+55, 1e+56, 1e+57, 1e+58, 1e+59, 1e+60,
|
||||
1e+61, 1e+62, 1e+63, 1e+64, 1e+65, 1e+66, 1e+67, 1e+68, 1e+69, 1e+70, 1e+71, 1e+72, 1e+73, 1e+74, 1e+75, 1e+76, 1e+77, 1e+78, 1e+79, 1e+80,
|
||||
1e+81, 1e+82, 1e+83, 1e+84, 1e+85, 1e+86, 1e+87, 1e+88, 1e+89, 1e+90, 1e+91, 1e+92, 1e+93, 1e+94, 1e+95, 1e+96, 1e+97, 1e+98, 1e+99, 1e+100,
|
||||
1e+101,1e+102,1e+103,1e+104,1e+105,1e+106,1e+107,1e+108,1e+109,1e+110,1e+111,1e+112,1e+113,1e+114,1e+115,1e+116,1e+117,1e+118,1e+119,1e+120,
|
||||
1e+121,1e+122,1e+123,1e+124,1e+125,1e+126,1e+127,1e+128,1e+129,1e+130,1e+131,1e+132,1e+133,1e+134,1e+135,1e+136,1e+137,1e+138,1e+139,1e+140,
|
||||
1e+141,1e+142,1e+143,1e+144,1e+145,1e+146,1e+147,1e+148,1e+149,1e+150,1e+151,1e+152,1e+153,1e+154,1e+155,1e+156,1e+157,1e+158,1e+159,1e+160,
|
||||
1e+161,1e+162,1e+163,1e+164,1e+165,1e+166,1e+167,1e+168,1e+169,1e+170,1e+171,1e+172,1e+173,1e+174,1e+175,1e+176,1e+177,1e+178,1e+179,1e+180,
|
||||
1e+181,1e+182,1e+183,1e+184,1e+185,1e+186,1e+187,1e+188,1e+189,1e+190,1e+191,1e+192,1e+193,1e+194,1e+195,1e+196,1e+197,1e+198,1e+199,1e+200,
|
||||
1e+201,1e+202,1e+203,1e+204,1e+205,1e+206,1e+207,1e+208,1e+209,1e+210,1e+211,1e+212,1e+213,1e+214,1e+215,1e+216,1e+217,1e+218,1e+219,1e+220,
|
||||
1e+221,1e+222,1e+223,1e+224,1e+225,1e+226,1e+227,1e+228,1e+229,1e+230,1e+231,1e+232,1e+233,1e+234,1e+235,1e+236,1e+237,1e+238,1e+239,1e+240,
|
||||
1e+241,1e+242,1e+243,1e+244,1e+245,1e+246,1e+247,1e+248,1e+249,1e+250,1e+251,1e+252,1e+253,1e+254,1e+255,1e+256,1e+257,1e+258,1e+259,1e+260,
|
||||
1e+261,1e+262,1e+263,1e+264,1e+265,1e+266,1e+267,1e+268,1e+269,1e+270,1e+271,1e+272,1e+273,1e+274,1e+275,1e+276,1e+277,1e+278,1e+279,1e+280,
|
||||
1e+281,1e+282,1e+283,1e+284,1e+285,1e+286,1e+287,1e+288,1e+289,1e+290,1e+291,1e+292,1e+293,1e+294,1e+295,1e+296,1e+297,1e+298,1e+299,1e+300,
|
||||
1e+301,1e+302,1e+303,1e+304,1e+305,1e+306,1e+307,1e+308
|
||||
};
|
||||
RAPIDJSON_ASSERT(n >= 0 && n <= 308);
|
||||
return e[n];
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_POW10_
|
||||
@@ -0,0 +1,177 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_INTERNAL_STACK_H_
|
||||
#define RAPIDJSON_INTERNAL_STACK_H_
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
namespace internal {
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Stack
|
||||
|
||||
//! A type-unsafe stack for storing different types of data.
|
||||
/*! \tparam Allocator Allocator for allocating stack memory.
|
||||
*/
|
||||
template <typename Allocator>
|
||||
class Stack {
|
||||
public:
|
||||
// Optimization note: Do not allocate memory for stack_ in constructor.
|
||||
// Do it lazily when first Push() -> Expand() -> Resize().
|
||||
Stack(Allocator* allocator, size_t stackCapacity) : allocator_(allocator), ownAllocator_(0), stack_(0), stackTop_(0), stackEnd_(0), initialCapacity_(stackCapacity) {
|
||||
RAPIDJSON_ASSERT(stackCapacity > 0);
|
||||
}
|
||||
|
||||
#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
|
||||
Stack(Stack&& rhs)
|
||||
: allocator_(rhs.allocator_),
|
||||
ownAllocator_(rhs.ownAllocator_),
|
||||
stack_(rhs.stack_),
|
||||
stackTop_(rhs.stackTop_),
|
||||
stackEnd_(rhs.stackEnd_),
|
||||
initialCapacity_(rhs.initialCapacity_)
|
||||
{
|
||||
rhs.allocator_ = 0;
|
||||
rhs.ownAllocator_ = 0;
|
||||
rhs.stack_ = 0;
|
||||
rhs.stackTop_ = 0;
|
||||
rhs.stackEnd_ = 0;
|
||||
rhs.initialCapacity_ = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
~Stack() {
|
||||
Destroy();
|
||||
}
|
||||
|
||||
#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
|
||||
Stack& operator=(Stack&& rhs) {
|
||||
if (&rhs != this)
|
||||
{
|
||||
Destroy();
|
||||
|
||||
allocator_ = rhs.allocator_;
|
||||
ownAllocator_ = rhs.ownAllocator_;
|
||||
stack_ = rhs.stack_;
|
||||
stackTop_ = rhs.stackTop_;
|
||||
stackEnd_ = rhs.stackEnd_;
|
||||
initialCapacity_ = rhs.initialCapacity_;
|
||||
|
||||
rhs.allocator_ = 0;
|
||||
rhs.ownAllocator_ = 0;
|
||||
rhs.stack_ = 0;
|
||||
rhs.stackTop_ = 0;
|
||||
rhs.stackEnd_ = 0;
|
||||
rhs.initialCapacity_ = 0;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
||||
void Clear() { stackTop_ = stack_; }
|
||||
|
||||
void ShrinkToFit() {
|
||||
if (Empty()) {
|
||||
// If the stack is empty, completely deallocate the memory.
|
||||
Allocator::Free(stack_);
|
||||
stack_ = 0;
|
||||
stackTop_ = 0;
|
||||
stackEnd_ = 0;
|
||||
}
|
||||
else
|
||||
Resize(GetSize());
|
||||
}
|
||||
|
||||
// Optimization note: try to minimize the size of this function for force inline.
|
||||
// Expansion is run very infrequently, so it is moved to another (probably non-inline) function.
|
||||
template<typename T>
|
||||
RAPIDJSON_FORCEINLINE T* Push(size_t count = 1) {
|
||||
// Expand the stack if needed
|
||||
if (stackTop_ + sizeof(T) * count >= stackEnd_)
|
||||
Expand<T>(count);
|
||||
|
||||
T* ret = reinterpret_cast<T*>(stackTop_);
|
||||
stackTop_ += sizeof(T) * count;
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T* Pop(size_t count) {
|
||||
RAPIDJSON_ASSERT(GetSize() >= count * sizeof(T));
|
||||
stackTop_ -= count * sizeof(T);
|
||||
return reinterpret_cast<T*>(stackTop_);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T* Top() {
|
||||
RAPIDJSON_ASSERT(GetSize() >= sizeof(T));
|
||||
return reinterpret_cast<T*>(stackTop_ - sizeof(T));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T* Bottom() { return (T*)stack_; }
|
||||
|
||||
Allocator& GetAllocator() { return *allocator_; }
|
||||
bool Empty() const { return stackTop_ == stack_; }
|
||||
size_t GetSize() const { return static_cast<size_t>(stackTop_ - stack_); }
|
||||
size_t GetCapacity() const { return static_cast<size_t>(stackEnd_ - stack_); }
|
||||
|
||||
private:
|
||||
template<typename T>
|
||||
void Expand(size_t count) {
|
||||
// Only expand the capacity if the current stack exists. Otherwise just create a stack with initial capacity.
|
||||
size_t newCapacity;
|
||||
if (stack_ == 0) {
|
||||
if (!allocator_)
|
||||
ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator());
|
||||
newCapacity = initialCapacity_;
|
||||
} else {
|
||||
newCapacity = GetCapacity();
|
||||
newCapacity += (newCapacity + 1) / 2;
|
||||
}
|
||||
size_t newSize = GetSize() + sizeof(T) * count;
|
||||
if (newCapacity < newSize)
|
||||
newCapacity = newSize;
|
||||
|
||||
Resize(newCapacity);
|
||||
}
|
||||
|
||||
void Resize(size_t newCapacity) {
|
||||
const size_t size = GetSize(); // Backup the current size
|
||||
stack_ = (char*)allocator_->Realloc(stack_, GetCapacity(), newCapacity);
|
||||
stackTop_ = stack_ + size;
|
||||
stackEnd_ = stack_ + newCapacity;
|
||||
}
|
||||
|
||||
void Destroy() {
|
||||
Allocator::Free(stack_);
|
||||
RAPIDJSON_DELETE(ownAllocator_); // Only delete if it is owned by the stack
|
||||
}
|
||||
|
||||
// Prohibit copy constructor & assignment operator.
|
||||
Stack(const Stack&);
|
||||
Stack& operator=(const Stack&);
|
||||
|
||||
Allocator* allocator_;
|
||||
Allocator* ownAllocator_;
|
||||
char *stack_;
|
||||
char *stackTop_;
|
||||
char *stackEnd_;
|
||||
size_t initialCapacity_;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_STACK_H_
|
||||
@@ -0,0 +1,37 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_INTERNAL_STRFUNC_H_
|
||||
#define RAPIDJSON_INTERNAL_STRFUNC_H_
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
namespace internal {
|
||||
|
||||
//! Custom strlen() which works on different character types.
|
||||
/*! \tparam Ch Character type (e.g. char, wchar_t, short)
|
||||
\param s Null-terminated input string.
|
||||
\return Number of characters in the string.
|
||||
\note This has the same semantics as strlen(), the return value is not number of Unicode codepoints.
|
||||
*/
|
||||
template <typename Ch>
|
||||
inline SizeType StrLen(const Ch* s) {
|
||||
const Ch* p = s;
|
||||
while (*p) ++p;
|
||||
return SizeType(p - s);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_INTERNAL_STRFUNC_H_
|
||||
@@ -0,0 +1,265 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_STRTOD_
|
||||
#define RAPIDJSON_STRTOD_
|
||||
|
||||
#include "../rapidjson.h"
|
||||
#include "ieee754.h"
|
||||
#include "biginteger.h"
|
||||
#include "diyfp.h"
|
||||
#include "pow10.h"
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
namespace internal {
|
||||
|
||||
inline double FastPath(double significand, int exp) {
|
||||
if (exp < -308)
|
||||
return 0.0;
|
||||
else if (exp >= 0)
|
||||
return significand * internal::Pow10(exp);
|
||||
else
|
||||
return significand / internal::Pow10(-exp);
|
||||
}
|
||||
|
||||
inline double StrtodNormalPrecision(double d, int p) {
|
||||
if (p < -308) {
|
||||
// Prevent expSum < -308, making Pow10(p) = 0
|
||||
d = FastPath(d, -308);
|
||||
d = FastPath(d, p + 308);
|
||||
}
|
||||
else
|
||||
d = FastPath(d, p);
|
||||
return d;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T Min3(T a, T b, T c) {
|
||||
T m = a;
|
||||
if (m > b) m = b;
|
||||
if (m > c) m = c;
|
||||
return m;
|
||||
}
|
||||
|
||||
inline int CheckWithinHalfULP(double b, const BigInteger& d, int dExp) {
|
||||
const Double db(b);
|
||||
const uint64_t bInt = db.IntegerSignificand();
|
||||
const int bExp = db.IntegerExponent();
|
||||
const int hExp = bExp - 1;
|
||||
|
||||
int dS_Exp2 = 0, dS_Exp5 = 0, bS_Exp2 = 0, bS_Exp5 = 0, hS_Exp2 = 0, hS_Exp5 = 0;
|
||||
|
||||
// Adjust for decimal exponent
|
||||
if (dExp >= 0) {
|
||||
dS_Exp2 += dExp;
|
||||
dS_Exp5 += dExp;
|
||||
}
|
||||
else {
|
||||
bS_Exp2 -= dExp;
|
||||
bS_Exp5 -= dExp;
|
||||
hS_Exp2 -= dExp;
|
||||
hS_Exp5 -= dExp;
|
||||
}
|
||||
|
||||
// Adjust for binary exponent
|
||||
if (bExp >= 0)
|
||||
bS_Exp2 += bExp;
|
||||
else {
|
||||
dS_Exp2 -= bExp;
|
||||
hS_Exp2 -= bExp;
|
||||
}
|
||||
|
||||
// Adjust for half ulp exponent
|
||||
if (hExp >= 0)
|
||||
hS_Exp2 += hExp;
|
||||
else {
|
||||
dS_Exp2 -= hExp;
|
||||
bS_Exp2 -= hExp;
|
||||
}
|
||||
|
||||
// Remove common power of two factor from all three scaled values
|
||||
int common_Exp2 = Min3(dS_Exp2, bS_Exp2, hS_Exp2);
|
||||
dS_Exp2 -= common_Exp2;
|
||||
bS_Exp2 -= common_Exp2;
|
||||
hS_Exp2 -= common_Exp2;
|
||||
|
||||
BigInteger dS = d;
|
||||
dS.MultiplyPow5(dS_Exp5) <<= dS_Exp2;
|
||||
|
||||
BigInteger bS(bInt);
|
||||
bS.MultiplyPow5(bS_Exp5) <<= bS_Exp2;
|
||||
|
||||
BigInteger hS(1);
|
||||
hS.MultiplyPow5(hS_Exp5) <<= hS_Exp2;
|
||||
|
||||
BigInteger delta(0);
|
||||
dS.Difference(bS, &delta);
|
||||
|
||||
return delta.Compare(hS);
|
||||
}
|
||||
|
||||
inline bool StrtodFast(double d, int p, double* result) {
|
||||
// Use fast path for string-to-double conversion if possible
|
||||
// see http://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/
|
||||
if (p > 22 && p < 22 + 16) {
|
||||
// Fast Path Cases In Disguise
|
||||
d *= internal::Pow10(p - 22);
|
||||
p = 22;
|
||||
}
|
||||
|
||||
if (p >= -22 && p <= 22 && d <= 9007199254740991.0) { // 2^53 - 1
|
||||
*result = FastPath(d, p);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
// Compute an approximation and see if it is within 1/2 ULP
|
||||
inline bool StrtodDiyFp(const char* decimals, size_t length, size_t decimalPosition, int exp, double* result) {
|
||||
uint64_t significand = 0;
|
||||
size_t i = 0; // 2^64 - 1 = 18446744073709551615, 1844674407370955161 = 0x1999999999999999
|
||||
for (; i < length; i++) {
|
||||
if (significand > RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) ||
|
||||
(significand == RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) && decimals[i] > '5'))
|
||||
break;
|
||||
significand = significand * 10 + (decimals[i] - '0');
|
||||
}
|
||||
|
||||
if (i < length && decimals[i] >= '5') // Rounding
|
||||
significand++;
|
||||
|
||||
size_t remaining = length - i;
|
||||
const unsigned kUlpShift = 3;
|
||||
const unsigned kUlp = 1 << kUlpShift;
|
||||
int error = (remaining == 0) ? 0 : kUlp / 2;
|
||||
|
||||
DiyFp v(significand, 0);
|
||||
v = v.Normalize();
|
||||
error <<= -v.e;
|
||||
|
||||
const int dExp = (int)decimalPosition - (int)i + exp;
|
||||
|
||||
int actualExp;
|
||||
DiyFp cachedPower = GetCachedPower10(dExp, &actualExp);
|
||||
if (actualExp != dExp) {
|
||||
static const DiyFp kPow10[] = {
|
||||
DiyFp(RAPIDJSON_UINT64_C2(0xa0000000, 00000000), -60), // 10^1
|
||||
DiyFp(RAPIDJSON_UINT64_C2(0xc8000000, 00000000), -57), // 10^2
|
||||
DiyFp(RAPIDJSON_UINT64_C2(0xfa000000, 00000000), -54), // 10^3
|
||||
DiyFp(RAPIDJSON_UINT64_C2(0x9c400000, 00000000), -50), // 10^4
|
||||
DiyFp(RAPIDJSON_UINT64_C2(0xc3500000, 00000000), -47), // 10^5
|
||||
DiyFp(RAPIDJSON_UINT64_C2(0xf4240000, 00000000), -44), // 10^6
|
||||
DiyFp(RAPIDJSON_UINT64_C2(0x98968000, 00000000), -40) // 10^7
|
||||
};
|
||||
int adjustment = dExp - actualExp - 1;
|
||||
RAPIDJSON_ASSERT(adjustment >= 0 && adjustment < 7);
|
||||
v = v * kPow10[adjustment];
|
||||
if (length + adjustment > 19) // has more digits than decimal digits in 64-bit
|
||||
error += kUlp / 2;
|
||||
}
|
||||
|
||||
v = v * cachedPower;
|
||||
|
||||
error += kUlp + (error == 0 ? 0 : 1);
|
||||
|
||||
const int oldExp = v.e;
|
||||
v = v.Normalize();
|
||||
error <<= oldExp - v.e;
|
||||
|
||||
const unsigned effectiveSignificandSize = Double::EffectiveSignificandSize(64 + v.e);
|
||||
unsigned precisionSize = 64 - effectiveSignificandSize;
|
||||
if (precisionSize + kUlpShift >= 64) {
|
||||
unsigned scaleExp = (precisionSize + kUlpShift) - 63;
|
||||
v.f >>= scaleExp;
|
||||
v.e += scaleExp;
|
||||
error = (error >> scaleExp) + 1 + kUlp;
|
||||
precisionSize -= scaleExp;
|
||||
}
|
||||
|
||||
DiyFp rounded(v.f >> precisionSize, v.e + precisionSize);
|
||||
const uint64_t precisionBits = (v.f & ((uint64_t(1) << precisionSize) - 1)) * kUlp;
|
||||
const uint64_t halfWay = (uint64_t(1) << (precisionSize - 1)) * kUlp;
|
||||
if (precisionBits >= halfWay + error)
|
||||
rounded.f++;
|
||||
|
||||
*result = rounded.ToDouble();
|
||||
|
||||
return halfWay - error >= precisionBits || precisionBits >= halfWay + error;
|
||||
}
|
||||
|
||||
inline double StrtodBigInteger(double approx, const char* decimals, size_t length, size_t decimalPosition, int exp) {
|
||||
const BigInteger dInt(decimals, length);
|
||||
const int dExp = (int)decimalPosition - (int)length + exp;
|
||||
Double a(approx);
|
||||
int cmp = CheckWithinHalfULP(a.Value(), dInt, dExp);
|
||||
if (cmp < 0)
|
||||
return a.Value(); // within half ULP
|
||||
else if (cmp == 0) {
|
||||
// Round towards even
|
||||
if (a.Significand() & 1)
|
||||
return a.NextPositiveDouble();
|
||||
else
|
||||
return a.Value();
|
||||
}
|
||||
else // adjustment
|
||||
return a.NextPositiveDouble();
|
||||
}
|
||||
|
||||
inline double StrtodFullPrecision(double d, int p, const char* decimals, size_t length, size_t decimalPosition, int exp) {
|
||||
RAPIDJSON_ASSERT(d >= 0.0);
|
||||
RAPIDJSON_ASSERT(length >= 1);
|
||||
|
||||
double result;
|
||||
if (StrtodFast(d, p, &result))
|
||||
return result;
|
||||
|
||||
// Trim leading zeros
|
||||
while (*decimals == '0' && length > 1) {
|
||||
length--;
|
||||
decimals++;
|
||||
decimalPosition--;
|
||||
}
|
||||
|
||||
// Trim trailing zeros
|
||||
while (decimals[length - 1] == '0' && length > 1) {
|
||||
length--;
|
||||
decimalPosition--;
|
||||
exp++;
|
||||
}
|
||||
|
||||
// Trim right-most digits
|
||||
const int kMaxDecimalDigit = 780;
|
||||
if ((int)length > kMaxDecimalDigit) {
|
||||
int delta = (int(length) - kMaxDecimalDigit);
|
||||
exp += delta;
|
||||
decimalPosition -= delta;
|
||||
length = kMaxDecimalDigit;
|
||||
}
|
||||
|
||||
// If too small, underflow to zero
|
||||
if (int(length) + exp < -324)
|
||||
return 0.0;
|
||||
|
||||
if (StrtodDiyFp(decimals, length, decimalPosition, exp, &result))
|
||||
return result;
|
||||
|
||||
// Use approximation from StrtodDiyFp and make adjustment with BigInteger comparison
|
||||
return StrtodBigInteger(result, decimals, length, decimalPosition, exp);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_STRTOD_
|
||||
@@ -0,0 +1,70 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_MEMORYBUFFER_H_
|
||||
#define RAPIDJSON_MEMORYBUFFER_H_
|
||||
|
||||
#include "rapidjson.h"
|
||||
#include "internal/stack.h"
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
|
||||
//! Represents an in-memory output byte stream.
|
||||
/*!
|
||||
This class is mainly for being wrapped by EncodedOutputStream or AutoUTFOutputStream.
|
||||
|
||||
It is similar to FileWriteBuffer but the destination is an in-memory buffer instead of a file.
|
||||
|
||||
Differences between MemoryBuffer and StringBuffer:
|
||||
1. StringBuffer has Encoding but MemoryBuffer is only a byte buffer.
|
||||
2. StringBuffer::GetString() returns a null-terminated string. MemoryBuffer::GetBuffer() returns a buffer without terminator.
|
||||
|
||||
\tparam Allocator type for allocating memory buffer.
|
||||
\note implements Stream concept
|
||||
*/
|
||||
template <typename Allocator = CrtAllocator>
|
||||
struct GenericMemoryBuffer {
|
||||
typedef char Ch; // byte
|
||||
|
||||
GenericMemoryBuffer(Allocator* allocator = 0, size_t capacity = kDefaultCapacity) : stack_(allocator, capacity) {}
|
||||
|
||||
void Put(Ch c) { *stack_.template Push<Ch>() = c; }
|
||||
void Flush() {}
|
||||
|
||||
void Clear() { stack_.Clear(); }
|
||||
void ShrinkToFit() { stack_.ShrinkToFit(); }
|
||||
Ch* Push(size_t count) { return stack_.template Push<Ch>(count); }
|
||||
void Pop(size_t count) { stack_.template Pop<Ch>(count); }
|
||||
|
||||
const Ch* GetBuffer() const {
|
||||
return stack_.template Bottom<Ch>();
|
||||
}
|
||||
|
||||
size_t GetSize() const { return stack_.GetSize(); }
|
||||
|
||||
static const size_t kDefaultCapacity = 256;
|
||||
mutable internal::Stack<Allocator> stack_;
|
||||
};
|
||||
|
||||
typedef GenericMemoryBuffer<> MemoryBuffer;
|
||||
|
||||
//! Implement specialized version of PutN() with memset() for better performance.
|
||||
template<>
|
||||
inline void PutN(MemoryBuffer& memoryBuffer, char c, size_t n) {
|
||||
std::memset(memoryBuffer.stack_.Push<char>(n), c, n * sizeof(c));
|
||||
}
|
||||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_MEMORYBUFFER_H_
|
||||
@@ -0,0 +1,61 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_MEMORYSTREAM_H_
|
||||
#define RAPIDJSON_MEMORYSTREAM_H_
|
||||
|
||||
#include "rapidjson.h"
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
|
||||
//! Represents an in-memory input byte stream.
|
||||
/*!
|
||||
This class is mainly for being wrapped by EncodedInputStream or AutoUTFInputStream.
|
||||
|
||||
It is similar to FileReadBuffer but the source is an in-memory buffer instead of a file.
|
||||
|
||||
Differences between MemoryStream and StringStream:
|
||||
1. StringStream has encoding but MemoryStream is a byte stream.
|
||||
2. MemoryStream needs size of the source buffer and the buffer don't need to be null terminated. StringStream assume null-terminated string as source.
|
||||
3. MemoryStream supports Peek4() for encoding detection. StringStream is specified with an encoding so it should not have Peek4().
|
||||
\note implements Stream concept
|
||||
*/
|
||||
struct MemoryStream {
|
||||
typedef char Ch; // byte
|
||||
|
||||
MemoryStream(const Ch *src, size_t size) : src_(src), begin_(src), end_(src + size), size_(size) {}
|
||||
|
||||
Ch Peek() const { return (src_ == end_) ? '\0' : *src_; }
|
||||
Ch Take() { return (src_ == end_) ? '\0' : *src_++; }
|
||||
size_t Tell() const { return static_cast<size_t>(src_ - begin_); }
|
||||
|
||||
Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
|
||||
void Put(Ch) { RAPIDJSON_ASSERT(false); }
|
||||
void Flush() { RAPIDJSON_ASSERT(false); }
|
||||
size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
|
||||
|
||||
// For encoding detection only.
|
||||
const Ch* Peek4() const {
|
||||
return Tell() + 4 <= size_ ? src_ : 0;
|
||||
}
|
||||
|
||||
const Ch* src_; //!< Current read position.
|
||||
const Ch* begin_; //!< Original head of the string.
|
||||
const Ch* end_; //!< End of stream.
|
||||
size_t size_; //!< Size of the stream.
|
||||
};
|
||||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_MEMORYBUFFER_H_
|
||||
@@ -0,0 +1,316 @@
|
||||
// ISO C9x compliant inttypes.h for Microsoft Visual Studio
|
||||
// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124
|
||||
//
|
||||
// Copyright (c) 2006-2013 Alexander Chemeris
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the product nor the names of its contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// The above software in this distribution may have been modified by
|
||||
// THL A29 Limited ("Tencent Modifications").
|
||||
// All Tencent Modifications are Copyright (C) 2015 THL A29 Limited.
|
||||
|
||||
#ifndef _MSC_VER // [
|
||||
#error "Use this header only with Microsoft Visual C++ compilers!"
|
||||
#endif // _MSC_VER ]
|
||||
|
||||
#ifndef _MSC_INTTYPES_H_ // [
|
||||
#define _MSC_INTTYPES_H_
|
||||
|
||||
#if _MSC_VER > 1000
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "stdint.h"
|
||||
|
||||
// miloyip: VC supports inttypes.h since VC2013
|
||||
#if _MSC_VER >= 1800
|
||||
#include <inttypes.h>
|
||||
#else
|
||||
|
||||
// 7.8 Format conversion of integer types
|
||||
|
||||
typedef struct {
|
||||
intmax_t quot;
|
||||
intmax_t rem;
|
||||
} imaxdiv_t;
|
||||
|
||||
// 7.8.1 Macros for format specifiers
|
||||
|
||||
#if !defined(__cplusplus) || defined(__STDC_FORMAT_MACROS) // [ See footnote 185 at page 198
|
||||
|
||||
// The fprintf macros for signed integers are:
|
||||
#define PRId8 "d"
|
||||
#define PRIi8 "i"
|
||||
#define PRIdLEAST8 "d"
|
||||
#define PRIiLEAST8 "i"
|
||||
#define PRIdFAST8 "d"
|
||||
#define PRIiFAST8 "i"
|
||||
|
||||
#define PRId16 "hd"
|
||||
#define PRIi16 "hi"
|
||||
#define PRIdLEAST16 "hd"
|
||||
#define PRIiLEAST16 "hi"
|
||||
#define PRIdFAST16 "hd"
|
||||
#define PRIiFAST16 "hi"
|
||||
|
||||
#define PRId32 "I32d"
|
||||
#define PRIi32 "I32i"
|
||||
#define PRIdLEAST32 "I32d"
|
||||
#define PRIiLEAST32 "I32i"
|
||||
#define PRIdFAST32 "I32d"
|
||||
#define PRIiFAST32 "I32i"
|
||||
|
||||
#define PRId64 "I64d"
|
||||
#define PRIi64 "I64i"
|
||||
#define PRIdLEAST64 "I64d"
|
||||
#define PRIiLEAST64 "I64i"
|
||||
#define PRIdFAST64 "I64d"
|
||||
#define PRIiFAST64 "I64i"
|
||||
|
||||
#define PRIdMAX "I64d"
|
||||
#define PRIiMAX "I64i"
|
||||
|
||||
#define PRIdPTR "Id"
|
||||
#define PRIiPTR "Ii"
|
||||
|
||||
// The fprintf macros for unsigned integers are:
|
||||
#define PRIo8 "o"
|
||||
#define PRIu8 "u"
|
||||
#define PRIx8 "x"
|
||||
#define PRIX8 "X"
|
||||
#define PRIoLEAST8 "o"
|
||||
#define PRIuLEAST8 "u"
|
||||
#define PRIxLEAST8 "x"
|
||||
#define PRIXLEAST8 "X"
|
||||
#define PRIoFAST8 "o"
|
||||
#define PRIuFAST8 "u"
|
||||
#define PRIxFAST8 "x"
|
||||
#define PRIXFAST8 "X"
|
||||
|
||||
#define PRIo16 "ho"
|
||||
#define PRIu16 "hu"
|
||||
#define PRIx16 "hx"
|
||||
#define PRIX16 "hX"
|
||||
#define PRIoLEAST16 "ho"
|
||||
#define PRIuLEAST16 "hu"
|
||||
#define PRIxLEAST16 "hx"
|
||||
#define PRIXLEAST16 "hX"
|
||||
#define PRIoFAST16 "ho"
|
||||
#define PRIuFAST16 "hu"
|
||||
#define PRIxFAST16 "hx"
|
||||
#define PRIXFAST16 "hX"
|
||||
|
||||
#define PRIo32 "I32o"
|
||||
#define PRIu32 "I32u"
|
||||
#define PRIx32 "I32x"
|
||||
#define PRIX32 "I32X"
|
||||
#define PRIoLEAST32 "I32o"
|
||||
#define PRIuLEAST32 "I32u"
|
||||
#define PRIxLEAST32 "I32x"
|
||||
#define PRIXLEAST32 "I32X"
|
||||
#define PRIoFAST32 "I32o"
|
||||
#define PRIuFAST32 "I32u"
|
||||
#define PRIxFAST32 "I32x"
|
||||
#define PRIXFAST32 "I32X"
|
||||
|
||||
#define PRIo64 "I64o"
|
||||
#define PRIu64 "I64u"
|
||||
#define PRIx64 "I64x"
|
||||
#define PRIX64 "I64X"
|
||||
#define PRIoLEAST64 "I64o"
|
||||
#define PRIuLEAST64 "I64u"
|
||||
#define PRIxLEAST64 "I64x"
|
||||
#define PRIXLEAST64 "I64X"
|
||||
#define PRIoFAST64 "I64o"
|
||||
#define PRIuFAST64 "I64u"
|
||||
#define PRIxFAST64 "I64x"
|
||||
#define PRIXFAST64 "I64X"
|
||||
|
||||
#define PRIoMAX "I64o"
|
||||
#define PRIuMAX "I64u"
|
||||
#define PRIxMAX "I64x"
|
||||
#define PRIXMAX "I64X"
|
||||
|
||||
#define PRIoPTR "Io"
|
||||
#define PRIuPTR "Iu"
|
||||
#define PRIxPTR "Ix"
|
||||
#define PRIXPTR "IX"
|
||||
|
||||
// The fscanf macros for signed integers are:
|
||||
#define SCNd8 "d"
|
||||
#define SCNi8 "i"
|
||||
#define SCNdLEAST8 "d"
|
||||
#define SCNiLEAST8 "i"
|
||||
#define SCNdFAST8 "d"
|
||||
#define SCNiFAST8 "i"
|
||||
|
||||
#define SCNd16 "hd"
|
||||
#define SCNi16 "hi"
|
||||
#define SCNdLEAST16 "hd"
|
||||
#define SCNiLEAST16 "hi"
|
||||
#define SCNdFAST16 "hd"
|
||||
#define SCNiFAST16 "hi"
|
||||
|
||||
#define SCNd32 "ld"
|
||||
#define SCNi32 "li"
|
||||
#define SCNdLEAST32 "ld"
|
||||
#define SCNiLEAST32 "li"
|
||||
#define SCNdFAST32 "ld"
|
||||
#define SCNiFAST32 "li"
|
||||
|
||||
#define SCNd64 "I64d"
|
||||
#define SCNi64 "I64i"
|
||||
#define SCNdLEAST64 "I64d"
|
||||
#define SCNiLEAST64 "I64i"
|
||||
#define SCNdFAST64 "I64d"
|
||||
#define SCNiFAST64 "I64i"
|
||||
|
||||
#define SCNdMAX "I64d"
|
||||
#define SCNiMAX "I64i"
|
||||
|
||||
#ifdef _WIN64 // [
|
||||
# define SCNdPTR "I64d"
|
||||
# define SCNiPTR "I64i"
|
||||
#else // _WIN64 ][
|
||||
# define SCNdPTR "ld"
|
||||
# define SCNiPTR "li"
|
||||
#endif // _WIN64 ]
|
||||
|
||||
// The fscanf macros for unsigned integers are:
|
||||
#define SCNo8 "o"
|
||||
#define SCNu8 "u"
|
||||
#define SCNx8 "x"
|
||||
#define SCNX8 "X"
|
||||
#define SCNoLEAST8 "o"
|
||||
#define SCNuLEAST8 "u"
|
||||
#define SCNxLEAST8 "x"
|
||||
#define SCNXLEAST8 "X"
|
||||
#define SCNoFAST8 "o"
|
||||
#define SCNuFAST8 "u"
|
||||
#define SCNxFAST8 "x"
|
||||
#define SCNXFAST8 "X"
|
||||
|
||||
#define SCNo16 "ho"
|
||||
#define SCNu16 "hu"
|
||||
#define SCNx16 "hx"
|
||||
#define SCNX16 "hX"
|
||||
#define SCNoLEAST16 "ho"
|
||||
#define SCNuLEAST16 "hu"
|
||||
#define SCNxLEAST16 "hx"
|
||||
#define SCNXLEAST16 "hX"
|
||||
#define SCNoFAST16 "ho"
|
||||
#define SCNuFAST16 "hu"
|
||||
#define SCNxFAST16 "hx"
|
||||
#define SCNXFAST16 "hX"
|
||||
|
||||
#define SCNo32 "lo"
|
||||
#define SCNu32 "lu"
|
||||
#define SCNx32 "lx"
|
||||
#define SCNX32 "lX"
|
||||
#define SCNoLEAST32 "lo"
|
||||
#define SCNuLEAST32 "lu"
|
||||
#define SCNxLEAST32 "lx"
|
||||
#define SCNXLEAST32 "lX"
|
||||
#define SCNoFAST32 "lo"
|
||||
#define SCNuFAST32 "lu"
|
||||
#define SCNxFAST32 "lx"
|
||||
#define SCNXFAST32 "lX"
|
||||
|
||||
#define SCNo64 "I64o"
|
||||
#define SCNu64 "I64u"
|
||||
#define SCNx64 "I64x"
|
||||
#define SCNX64 "I64X"
|
||||
#define SCNoLEAST64 "I64o"
|
||||
#define SCNuLEAST64 "I64u"
|
||||
#define SCNxLEAST64 "I64x"
|
||||
#define SCNXLEAST64 "I64X"
|
||||
#define SCNoFAST64 "I64o"
|
||||
#define SCNuFAST64 "I64u"
|
||||
#define SCNxFAST64 "I64x"
|
||||
#define SCNXFAST64 "I64X"
|
||||
|
||||
#define SCNoMAX "I64o"
|
||||
#define SCNuMAX "I64u"
|
||||
#define SCNxMAX "I64x"
|
||||
#define SCNXMAX "I64X"
|
||||
|
||||
#ifdef _WIN64 // [
|
||||
# define SCNoPTR "I64o"
|
||||
# define SCNuPTR "I64u"
|
||||
# define SCNxPTR "I64x"
|
||||
# define SCNXPTR "I64X"
|
||||
#else // _WIN64 ][
|
||||
# define SCNoPTR "lo"
|
||||
# define SCNuPTR "lu"
|
||||
# define SCNxPTR "lx"
|
||||
# define SCNXPTR "lX"
|
||||
#endif // _WIN64 ]
|
||||
|
||||
#endif // __STDC_FORMAT_MACROS ]
|
||||
|
||||
// 7.8.2 Functions for greatest-width integer types
|
||||
|
||||
// 7.8.2.1 The imaxabs function
|
||||
#define imaxabs _abs64
|
||||
|
||||
// 7.8.2.2 The imaxdiv function
|
||||
|
||||
// This is modified version of div() function from Microsoft's div.c found
|
||||
// in %MSVC.NET%\crt\src\div.c
|
||||
#ifdef STATIC_IMAXDIV // [
|
||||
static
|
||||
#else // STATIC_IMAXDIV ][
|
||||
_inline
|
||||
#endif // STATIC_IMAXDIV ]
|
||||
imaxdiv_t __cdecl imaxdiv(intmax_t numer, intmax_t denom)
|
||||
{
|
||||
imaxdiv_t result;
|
||||
|
||||
result.quot = numer / denom;
|
||||
result.rem = numer % denom;
|
||||
|
||||
if (numer < 0 && result.rem > 0) {
|
||||
// did division wrong; must fix up
|
||||
++result.quot;
|
||||
result.rem -= denom;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// 7.8.2.3 The strtoimax and strtoumax functions
|
||||
#define strtoimax _strtoi64
|
||||
#define strtoumax _strtoui64
|
||||
|
||||
// 7.8.2.4 The wcstoimax and wcstoumax functions
|
||||
#define wcstoimax _wcstoi64
|
||||
#define wcstoumax _wcstoui64
|
||||
|
||||
#endif // _MSC_VER >= 1800
|
||||
|
||||
#endif // _MSC_INTTYPES_H_ ]
|
||||
@@ -0,0 +1,300 @@
|
||||
// ISO C9x compliant stdint.h for Microsoft Visual Studio
|
||||
// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124
|
||||
//
|
||||
// Copyright (c) 2006-2013 Alexander Chemeris
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the product nor the names of its contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// The above software in this distribution may have been modified by
|
||||
// THL A29 Limited ("Tencent Modifications").
|
||||
// All Tencent Modifications are Copyright (C) 2015 THL A29 Limited.
|
||||
|
||||
#ifndef _MSC_VER // [
|
||||
#error "Use this header only with Microsoft Visual C++ compilers!"
|
||||
#endif // _MSC_VER ]
|
||||
|
||||
#ifndef _MSC_STDINT_H_ // [
|
||||
#define _MSC_STDINT_H_
|
||||
|
||||
#if _MSC_VER > 1000
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
// miloyip: Originally Visual Studio 2010 uses its own stdint.h. However it generates warning with INT64_C(), so change to use this file for vs2010.
|
||||
#if _MSC_VER >= 1600 // [
|
||||
#include <stdint.h>
|
||||
|
||||
#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260
|
||||
|
||||
#undef INT8_C
|
||||
#undef INT16_C
|
||||
#undef INT32_C
|
||||
#undef INT64_C
|
||||
#undef UINT8_C
|
||||
#undef UINT16_C
|
||||
#undef UINT32_C
|
||||
#undef UINT64_C
|
||||
|
||||
// 7.18.4.1 Macros for minimum-width integer constants
|
||||
|
||||
#define INT8_C(val) val##i8
|
||||
#define INT16_C(val) val##i16
|
||||
#define INT32_C(val) val##i32
|
||||
#define INT64_C(val) val##i64
|
||||
|
||||
#define UINT8_C(val) val##ui8
|
||||
#define UINT16_C(val) val##ui16
|
||||
#define UINT32_C(val) val##ui32
|
||||
#define UINT64_C(val) val##ui64
|
||||
|
||||
// 7.18.4.2 Macros for greatest-width integer constants
|
||||
// These #ifndef's are needed to prevent collisions with <boost/cstdint.hpp>.
|
||||
// Check out Issue 9 for the details.
|
||||
#ifndef INTMAX_C // [
|
||||
# define INTMAX_C INT64_C
|
||||
#endif // INTMAX_C ]
|
||||
#ifndef UINTMAX_C // [
|
||||
# define UINTMAX_C UINT64_C
|
||||
#endif // UINTMAX_C ]
|
||||
|
||||
#endif // __STDC_CONSTANT_MACROS ]
|
||||
|
||||
#else // ] _MSC_VER >= 1700 [
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
// For Visual Studio 6 in C++ mode and for many Visual Studio versions when
|
||||
// compiling for ARM we should wrap <wchar.h> include with 'extern "C++" {}'
|
||||
// or compiler give many errors like this:
|
||||
// error C2733: second C linkage of overloaded function 'wmemchr' not allowed
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
# include <wchar.h>
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
// Define _W64 macros to mark types changing their size, like intptr_t.
|
||||
#ifndef _W64
|
||||
# if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300
|
||||
# define _W64 __w64
|
||||
# else
|
||||
# define _W64
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
||||
// 7.18.1 Integer types
|
||||
|
||||
// 7.18.1.1 Exact-width integer types
|
||||
|
||||
// Visual Studio 6 and Embedded Visual C++ 4 doesn't
|
||||
// realize that, e.g. char has the same size as __int8
|
||||
// so we give up on __intX for them.
|
||||
#if (_MSC_VER < 1300)
|
||||
typedef signed char int8_t;
|
||||
typedef signed short int16_t;
|
||||
typedef signed int int32_t;
|
||||
typedef unsigned char uint8_t;
|
||||
typedef unsigned short uint16_t;
|
||||
typedef unsigned int uint32_t;
|
||||
#else
|
||||
typedef signed __int8 int8_t;
|
||||
typedef signed __int16 int16_t;
|
||||
typedef signed __int32 int32_t;
|
||||
typedef unsigned __int8 uint8_t;
|
||||
typedef unsigned __int16 uint16_t;
|
||||
typedef unsigned __int32 uint32_t;
|
||||
#endif
|
||||
typedef signed __int64 int64_t;
|
||||
typedef unsigned __int64 uint64_t;
|
||||
|
||||
|
||||
// 7.18.1.2 Minimum-width integer types
|
||||
typedef int8_t int_least8_t;
|
||||
typedef int16_t int_least16_t;
|
||||
typedef int32_t int_least32_t;
|
||||
typedef int64_t int_least64_t;
|
||||
typedef uint8_t uint_least8_t;
|
||||
typedef uint16_t uint_least16_t;
|
||||
typedef uint32_t uint_least32_t;
|
||||
typedef uint64_t uint_least64_t;
|
||||
|
||||
// 7.18.1.3 Fastest minimum-width integer types
|
||||
typedef int8_t int_fast8_t;
|
||||
typedef int16_t int_fast16_t;
|
||||
typedef int32_t int_fast32_t;
|
||||
typedef int64_t int_fast64_t;
|
||||
typedef uint8_t uint_fast8_t;
|
||||
typedef uint16_t uint_fast16_t;
|
||||
typedef uint32_t uint_fast32_t;
|
||||
typedef uint64_t uint_fast64_t;
|
||||
|
||||
// 7.18.1.4 Integer types capable of holding object pointers
|
||||
#ifdef _WIN64 // [
|
||||
typedef signed __int64 intptr_t;
|
||||
typedef unsigned __int64 uintptr_t;
|
||||
#else // _WIN64 ][
|
||||
typedef _W64 signed int intptr_t;
|
||||
typedef _W64 unsigned int uintptr_t;
|
||||
#endif // _WIN64 ]
|
||||
|
||||
// 7.18.1.5 Greatest-width integer types
|
||||
typedef int64_t intmax_t;
|
||||
typedef uint64_t uintmax_t;
|
||||
|
||||
|
||||
// 7.18.2 Limits of specified-width integer types
|
||||
|
||||
#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259
|
||||
|
||||
// 7.18.2.1 Limits of exact-width integer types
|
||||
#define INT8_MIN ((int8_t)_I8_MIN)
|
||||
#define INT8_MAX _I8_MAX
|
||||
#define INT16_MIN ((int16_t)_I16_MIN)
|
||||
#define INT16_MAX _I16_MAX
|
||||
#define INT32_MIN ((int32_t)_I32_MIN)
|
||||
#define INT32_MAX _I32_MAX
|
||||
#define INT64_MIN ((int64_t)_I64_MIN)
|
||||
#define INT64_MAX _I64_MAX
|
||||
#define UINT8_MAX _UI8_MAX
|
||||
#define UINT16_MAX _UI16_MAX
|
||||
#define UINT32_MAX _UI32_MAX
|
||||
#define UINT64_MAX _UI64_MAX
|
||||
|
||||
// 7.18.2.2 Limits of minimum-width integer types
|
||||
#define INT_LEAST8_MIN INT8_MIN
|
||||
#define INT_LEAST8_MAX INT8_MAX
|
||||
#define INT_LEAST16_MIN INT16_MIN
|
||||
#define INT_LEAST16_MAX INT16_MAX
|
||||
#define INT_LEAST32_MIN INT32_MIN
|
||||
#define INT_LEAST32_MAX INT32_MAX
|
||||
#define INT_LEAST64_MIN INT64_MIN
|
||||
#define INT_LEAST64_MAX INT64_MAX
|
||||
#define UINT_LEAST8_MAX UINT8_MAX
|
||||
#define UINT_LEAST16_MAX UINT16_MAX
|
||||
#define UINT_LEAST32_MAX UINT32_MAX
|
||||
#define UINT_LEAST64_MAX UINT64_MAX
|
||||
|
||||
// 7.18.2.3 Limits of fastest minimum-width integer types
|
||||
#define INT_FAST8_MIN INT8_MIN
|
||||
#define INT_FAST8_MAX INT8_MAX
|
||||
#define INT_FAST16_MIN INT16_MIN
|
||||
#define INT_FAST16_MAX INT16_MAX
|
||||
#define INT_FAST32_MIN INT32_MIN
|
||||
#define INT_FAST32_MAX INT32_MAX
|
||||
#define INT_FAST64_MIN INT64_MIN
|
||||
#define INT_FAST64_MAX INT64_MAX
|
||||
#define UINT_FAST8_MAX UINT8_MAX
|
||||
#define UINT_FAST16_MAX UINT16_MAX
|
||||
#define UINT_FAST32_MAX UINT32_MAX
|
||||
#define UINT_FAST64_MAX UINT64_MAX
|
||||
|
||||
// 7.18.2.4 Limits of integer types capable of holding object pointers
|
||||
#ifdef _WIN64 // [
|
||||
# define INTPTR_MIN INT64_MIN
|
||||
# define INTPTR_MAX INT64_MAX
|
||||
# define UINTPTR_MAX UINT64_MAX
|
||||
#else // _WIN64 ][
|
||||
# define INTPTR_MIN INT32_MIN
|
||||
# define INTPTR_MAX INT32_MAX
|
||||
# define UINTPTR_MAX UINT32_MAX
|
||||
#endif // _WIN64 ]
|
||||
|
||||
// 7.18.2.5 Limits of greatest-width integer types
|
||||
#define INTMAX_MIN INT64_MIN
|
||||
#define INTMAX_MAX INT64_MAX
|
||||
#define UINTMAX_MAX UINT64_MAX
|
||||
|
||||
// 7.18.3 Limits of other integer types
|
||||
|
||||
#ifdef _WIN64 // [
|
||||
# define PTRDIFF_MIN _I64_MIN
|
||||
# define PTRDIFF_MAX _I64_MAX
|
||||
#else // _WIN64 ][
|
||||
# define PTRDIFF_MIN _I32_MIN
|
||||
# define PTRDIFF_MAX _I32_MAX
|
||||
#endif // _WIN64 ]
|
||||
|
||||
#define SIG_ATOMIC_MIN INT_MIN
|
||||
#define SIG_ATOMIC_MAX INT_MAX
|
||||
|
||||
#ifndef SIZE_MAX // [
|
||||
# ifdef _WIN64 // [
|
||||
# define SIZE_MAX _UI64_MAX
|
||||
# else // _WIN64 ][
|
||||
# define SIZE_MAX _UI32_MAX
|
||||
# endif // _WIN64 ]
|
||||
#endif // SIZE_MAX ]
|
||||
|
||||
// WCHAR_MIN and WCHAR_MAX are also defined in <wchar.h>
|
||||
#ifndef WCHAR_MIN // [
|
||||
# define WCHAR_MIN 0
|
||||
#endif // WCHAR_MIN ]
|
||||
#ifndef WCHAR_MAX // [
|
||||
# define WCHAR_MAX _UI16_MAX
|
||||
#endif // WCHAR_MAX ]
|
||||
|
||||
#define WINT_MIN 0
|
||||
#define WINT_MAX _UI16_MAX
|
||||
|
||||
#endif // __STDC_LIMIT_MACROS ]
|
||||
|
||||
|
||||
// 7.18.4 Limits of other integer types
|
||||
|
||||
#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260
|
||||
|
||||
// 7.18.4.1 Macros for minimum-width integer constants
|
||||
|
||||
#define INT8_C(val) val##i8
|
||||
#define INT16_C(val) val##i16
|
||||
#define INT32_C(val) val##i32
|
||||
#define INT64_C(val) val##i64
|
||||
|
||||
#define UINT8_C(val) val##ui8
|
||||
#define UINT16_C(val) val##ui16
|
||||
#define UINT32_C(val) val##ui32
|
||||
#define UINT64_C(val) val##ui64
|
||||
|
||||
// 7.18.4.2 Macros for greatest-width integer constants
|
||||
// These #ifndef's are needed to prevent collisions with <boost/cstdint.hpp>.
|
||||
// Check out Issue 9 for the details.
|
||||
#ifndef INTMAX_C // [
|
||||
# define INTMAX_C INT64_C
|
||||
#endif // INTMAX_C ]
|
||||
#ifndef UINTMAX_C // [
|
||||
# define UINTMAX_C UINT64_C
|
||||
#endif // UINTMAX_C ]
|
||||
|
||||
#endif // __STDC_CONSTANT_MACROS ]
|
||||
|
||||
#endif // _MSC_VER >= 1600 ]
|
||||
|
||||
#endif // _MSC_STDINT_H_ ]
|
||||
@@ -0,0 +1,207 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_PRETTYWRITER_H_
|
||||
#define RAPIDJSON_PRETTYWRITER_H_
|
||||
|
||||
#include "writer.h"
|
||||
|
||||
#ifdef __GNUC__
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(effc++)
|
||||
#endif
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
|
||||
//! Writer with indentation and spacing.
|
||||
/*!
|
||||
\tparam OutputStream Type of ouptut os.
|
||||
\tparam SourceEncoding Encoding of source string.
|
||||
\tparam TargetEncoding Encoding of output stream.
|
||||
\tparam StackAllocator Type of allocator for allocating memory of stack.
|
||||
*/
|
||||
template<typename OutputStream, typename SourceEncoding = UTF8<>, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator>
|
||||
class PrettyWriter : public Writer<OutputStream, SourceEncoding, TargetEncoding, StackAllocator> {
|
||||
public:
|
||||
typedef Writer<OutputStream, SourceEncoding, TargetEncoding, StackAllocator> Base;
|
||||
typedef typename Base::Ch Ch;
|
||||
|
||||
//! Constructor
|
||||
/*! \param os Output stream.
|
||||
\param allocator User supplied allocator. If it is null, it will create a private one.
|
||||
\param levelDepth Initial capacity of stack.
|
||||
*/
|
||||
PrettyWriter(OutputStream& os, StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) :
|
||||
Base(os, allocator, levelDepth), indentChar_(' '), indentCharCount_(4) {}
|
||||
|
||||
//! Set custom indentation.
|
||||
/*! \param indentChar Character for indentation. Must be whitespace character (' ', '\\t', '\\n', '\\r').
|
||||
\param indentCharCount Number of indent characters for each indentation level.
|
||||
\note The default indentation is 4 spaces.
|
||||
*/
|
||||
PrettyWriter& SetIndent(Ch indentChar, unsigned indentCharCount) {
|
||||
RAPIDJSON_ASSERT(indentChar == ' ' || indentChar == '\t' || indentChar == '\n' || indentChar == '\r');
|
||||
indentChar_ = indentChar;
|
||||
indentCharCount_ = indentCharCount;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/*! @name Implementation of Handler
|
||||
\see Handler
|
||||
*/
|
||||
//@{
|
||||
|
||||
bool Null() { PrettyPrefix(kNullType); return Base::WriteNull(); }
|
||||
bool Bool(bool b) { PrettyPrefix(b ? kTrueType : kFalseType); return Base::WriteBool(b); }
|
||||
bool Int(int i) { PrettyPrefix(kNumberType); return Base::WriteInt(i); }
|
||||
bool Uint(unsigned u) { PrettyPrefix(kNumberType); return Base::WriteUint(u); }
|
||||
bool Int64(int64_t i64) { PrettyPrefix(kNumberType); return Base::WriteInt64(i64); }
|
||||
bool Uint64(uint64_t u64) { PrettyPrefix(kNumberType); return Base::WriteUint64(u64); }
|
||||
bool Double(double d) { PrettyPrefix(kNumberType); return Base::WriteDouble(d); }
|
||||
|
||||
bool String(const Ch* str, SizeType length, bool copy = false) {
|
||||
(void)copy;
|
||||
PrettyPrefix(kStringType);
|
||||
return Base::WriteString(str, length);
|
||||
}
|
||||
|
||||
#if RAPIDJSON_HAS_STDSTRING
|
||||
bool String(const std::basic_string<Ch>& str) {
|
||||
return String(str.data(), SizeType(str.size()));
|
||||
}
|
||||
#endif
|
||||
|
||||
bool StartObject() {
|
||||
PrettyPrefix(kObjectType);
|
||||
new (Base::level_stack_.template Push<typename Base::Level>()) typename Base::Level(false);
|
||||
return Base::WriteStartObject();
|
||||
}
|
||||
|
||||
bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); }
|
||||
|
||||
bool EndObject(SizeType memberCount = 0) {
|
||||
(void)memberCount;
|
||||
RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level));
|
||||
RAPIDJSON_ASSERT(!Base::level_stack_.template Top<typename Base::Level>()->inArray);
|
||||
bool empty = Base::level_stack_.template Pop<typename Base::Level>(1)->valueCount == 0;
|
||||
|
||||
if (!empty) {
|
||||
Base::os_->Put('\n');
|
||||
WriteIndent();
|
||||
}
|
||||
bool ret = Base::WriteEndObject();
|
||||
(void)ret;
|
||||
RAPIDJSON_ASSERT(ret == true);
|
||||
if (Base::level_stack_.Empty()) // end of json text
|
||||
Base::os_->Flush();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StartArray() {
|
||||
PrettyPrefix(kArrayType);
|
||||
new (Base::level_stack_.template Push<typename Base::Level>()) typename Base::Level(true);
|
||||
return Base::WriteStartArray();
|
||||
}
|
||||
|
||||
bool EndArray(SizeType memberCount = 0) {
|
||||
(void)memberCount;
|
||||
RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level));
|
||||
RAPIDJSON_ASSERT(Base::level_stack_.template Top<typename Base::Level>()->inArray);
|
||||
bool empty = Base::level_stack_.template Pop<typename Base::Level>(1)->valueCount == 0;
|
||||
|
||||
if (!empty) {
|
||||
Base::os_->Put('\n');
|
||||
WriteIndent();
|
||||
}
|
||||
bool ret = Base::WriteEndArray();
|
||||
(void)ret;
|
||||
RAPIDJSON_ASSERT(ret == true);
|
||||
if (Base::level_stack_.Empty()) // end of json text
|
||||
Base::os_->Flush();
|
||||
return true;
|
||||
}
|
||||
|
||||
//@}
|
||||
|
||||
/*! @name Convenience extensions */
|
||||
//@{
|
||||
|
||||
//! Simpler but slower overload.
|
||||
bool String(const Ch* str) { return String(str, internal::StrLen(str)); }
|
||||
bool Key(const Ch* str) { return Key(str, internal::StrLen(str)); }
|
||||
|
||||
//@}
|
||||
protected:
|
||||
void PrettyPrefix(Type type) {
|
||||
(void)type;
|
||||
if (Base::level_stack_.GetSize() != 0) { // this value is not at root
|
||||
typename Base::Level* level = Base::level_stack_.template Top<typename Base::Level>();
|
||||
|
||||
if (level->inArray) {
|
||||
if (level->valueCount > 0) {
|
||||
Base::os_->Put(','); // add comma if it is not the first element in array
|
||||
Base::os_->Put('\n');
|
||||
}
|
||||
else
|
||||
Base::os_->Put('\n');
|
||||
WriteIndent();
|
||||
}
|
||||
else { // in object
|
||||
if (level->valueCount > 0) {
|
||||
if (level->valueCount % 2 == 0) {
|
||||
Base::os_->Put(',');
|
||||
Base::os_->Put('\n');
|
||||
}
|
||||
else {
|
||||
Base::os_->Put(':');
|
||||
Base::os_->Put(' ');
|
||||
}
|
||||
}
|
||||
else
|
||||
Base::os_->Put('\n');
|
||||
|
||||
if (level->valueCount % 2 == 0)
|
||||
WriteIndent();
|
||||
}
|
||||
if (!level->inArray && level->valueCount % 2 == 0)
|
||||
RAPIDJSON_ASSERT(type == kStringType); // if it's in object, then even number should be a name
|
||||
level->valueCount++;
|
||||
}
|
||||
else {
|
||||
RAPIDJSON_ASSERT(!Base::hasRoot_); // Should only has one and only one root.
|
||||
Base::hasRoot_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
void WriteIndent() {
|
||||
size_t count = (Base::level_stack_.GetSize() / sizeof(typename Base::Level)) * indentCharCount_;
|
||||
PutN(*Base::os_, indentChar_, count);
|
||||
}
|
||||
|
||||
Ch indentChar_;
|
||||
unsigned indentCharCount_;
|
||||
|
||||
private:
|
||||
// Prohibit copy constructor & assignment operator.
|
||||
PrettyWriter(const PrettyWriter&);
|
||||
PrettyWriter& operator=(const PrettyWriter&);
|
||||
};
|
||||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#ifdef __GNUC__
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
#endif // RAPIDJSON_RAPIDJSON_H_
|
||||
@@ -0,0 +1,620 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_RAPIDJSON_H_
|
||||
#define RAPIDJSON_RAPIDJSON_H_
|
||||
|
||||
/*!\file rapidjson.h
|
||||
\brief common definitions and configuration
|
||||
|
||||
\see RAPIDJSON_CONFIG
|
||||
*/
|
||||
|
||||
/*! \defgroup RAPIDJSON_CONFIG RapidJSON configuration
|
||||
\brief Configuration macros for library features
|
||||
|
||||
Some RapidJSON features are configurable to adapt the library to a wide
|
||||
variety of platforms, environments and usage scenarios. Most of the
|
||||
features can be configured in terms of overriden or predefined
|
||||
preprocessor macros at compile-time.
|
||||
|
||||
Some additional customization is available in the \ref RAPIDJSON_ERRORS APIs.
|
||||
|
||||
\note These macros should be given on the compiler command-line
|
||||
(where applicable) to avoid inconsistent values when compiling
|
||||
different translation units of a single application.
|
||||
*/
|
||||
|
||||
#include <cstdlib> // malloc(), realloc(), free(), size_t
|
||||
#include <cstring> // memset(), memcpy(), memmove(), memcmp()
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// RAPIDJSON_NAMESPACE_(BEGIN|END)
|
||||
/*! \def RAPIDJSON_NAMESPACE
|
||||
\ingroup RAPIDJSON_CONFIG
|
||||
\brief provide custom rapidjson namespace
|
||||
|
||||
In order to avoid symbol clashes and/or "One Definition Rule" errors
|
||||
between multiple inclusions of (different versions of) RapidJSON in
|
||||
a single binary, users can customize the name of the main RapidJSON
|
||||
namespace.
|
||||
|
||||
In case of a single nesting level, defining \c RAPIDJSON_NAMESPACE
|
||||
to a custom name (e.g. \c MyRapidJSON) is sufficient. If multiple
|
||||
levels are needed, both \ref RAPIDJSON_NAMESPACE_BEGIN and \ref
|
||||
RAPIDJSON_NAMESPACE_END need to be defined as well:
|
||||
|
||||
\code
|
||||
// in some .cpp file
|
||||
#define RAPIDJSON_NAMESPACE my::rapidjson
|
||||
#define RAPIDJSON_NAMESPACE_BEGIN namespace my { namespace rapidjson {
|
||||
#define RAPIDJSON_NAMESPACE_END } }
|
||||
#include "rapidjson/..."
|
||||
\endcode
|
||||
|
||||
\see rapidjson
|
||||
*/
|
||||
/*! \def RAPIDJSON_NAMESPACE_BEGIN
|
||||
\ingroup RAPIDJSON_CONFIG
|
||||
\brief provide custom rapidjson namespace (opening expression)
|
||||
\see RAPIDJSON_NAMESPACE
|
||||
*/
|
||||
/*! \def RAPIDJSON_NAMESPACE_END
|
||||
\ingroup RAPIDJSON_CONFIG
|
||||
\brief provide custom rapidjson namespace (closing expression)
|
||||
\see RAPIDJSON_NAMESPACE
|
||||
*/
|
||||
#ifndef RAPIDJSON_NAMESPACE
|
||||
#define RAPIDJSON_NAMESPACE rapidjson
|
||||
#endif
|
||||
#ifndef RAPIDJSON_NAMESPACE_BEGIN
|
||||
#define RAPIDJSON_NAMESPACE_BEGIN namespace RAPIDJSON_NAMESPACE {
|
||||
#endif
|
||||
#ifndef RAPIDJSON_NAMESPACE_END
|
||||
#define RAPIDJSON_NAMESPACE_END }
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// RAPIDJSON_NO_INT64DEFINE
|
||||
|
||||
/*! \def RAPIDJSON_NO_INT64DEFINE
|
||||
\ingroup RAPIDJSON_CONFIG
|
||||
\brief Use external 64-bit integer types.
|
||||
|
||||
RapidJSON requires the 64-bit integer types \c int64_t and \c uint64_t types
|
||||
to be available at global scope.
|
||||
|
||||
If users have their own definition, define RAPIDJSON_NO_INT64DEFINE to
|
||||
prevent RapidJSON from defining its own types.
|
||||
*/
|
||||
#ifndef RAPIDJSON_NO_INT64DEFINE
|
||||
//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN
|
||||
#ifdef _MSC_VER
|
||||
#include "msinttypes/stdint.h"
|
||||
#include "msinttypes/inttypes.h"
|
||||
#else
|
||||
// Other compilers should have this.
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
#endif
|
||||
//!@endcond
|
||||
#ifdef RAPIDJSON_DOXYGEN_RUNNING
|
||||
#define RAPIDJSON_NO_INT64DEFINE
|
||||
#endif
|
||||
#endif // RAPIDJSON_NO_INT64TYPEDEF
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// RAPIDJSON_FORCEINLINE
|
||||
|
||||
#ifndef RAPIDJSON_FORCEINLINE
|
||||
//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN
|
||||
#if defined(_MSC_VER) && !defined(NDEBUG)
|
||||
#define RAPIDJSON_FORCEINLINE __forceinline
|
||||
#elif defined(__GNUC__) && __GNUC__ >= 4 && !defined(NDEBUG)
|
||||
#define RAPIDJSON_FORCEINLINE __attribute__((always_inline))
|
||||
#else
|
||||
#define RAPIDJSON_FORCEINLINE
|
||||
#endif
|
||||
//!@endcond
|
||||
#endif // RAPIDJSON_FORCEINLINE
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// RAPIDJSON_ENDIAN
|
||||
#define RAPIDJSON_LITTLEENDIAN 0 //!< Little endian machine
|
||||
#define RAPIDJSON_BIGENDIAN 1 //!< Big endian machine
|
||||
|
||||
//! Endianness of the machine.
|
||||
/*!
|
||||
\def RAPIDJSON_ENDIAN
|
||||
\ingroup RAPIDJSON_CONFIG
|
||||
|
||||
GCC 4.6 provided macro for detecting endianness of the target machine. But other
|
||||
compilers may not have this. User can define RAPIDJSON_ENDIAN to either
|
||||
\ref RAPIDJSON_LITTLEENDIAN or \ref RAPIDJSON_BIGENDIAN.
|
||||
|
||||
Default detection implemented with reference to
|
||||
\li https://gcc.gnu.org/onlinedocs/gcc-4.6.0/cpp/Common-Predefined-Macros.html
|
||||
\li http://www.boost.org/doc/libs/1_42_0/boost/detail/endian.hpp
|
||||
*/
|
||||
#ifndef RAPIDJSON_ENDIAN
|
||||
// Detect with GCC 4.6's macro
|
||||
# ifdef __BYTE_ORDER__
|
||||
# if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN
|
||||
# elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||
# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN
|
||||
# else
|
||||
# error Unknown machine endianess detected. User needs to define RAPIDJSON_ENDIAN.
|
||||
# endif // __BYTE_ORDER__
|
||||
// Detect with GLIBC's endian.h
|
||||
# elif defined(__GLIBC__)
|
||||
# include <endian.h>
|
||||
# if (__BYTE_ORDER == __LITTLE_ENDIAN)
|
||||
# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN
|
||||
# elif (__BYTE_ORDER == __BIG_ENDIAN)
|
||||
# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN
|
||||
# else
|
||||
# error Unknown machine endianess detected. User needs to define RAPIDJSON_ENDIAN.
|
||||
# endif // __GLIBC__
|
||||
// Detect with _LITTLE_ENDIAN and _BIG_ENDIAN macro
|
||||
# elif defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN)
|
||||
# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN
|
||||
# elif defined(_BIG_ENDIAN) && !defined(_LITTLE_ENDIAN)
|
||||
# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN
|
||||
// Detect with architecture macros
|
||||
# elif defined(__sparc) || defined(__sparc__) || defined(_POWER) || defined(__powerpc__) || defined(__ppc__) || defined(__hpux) || defined(__hppa) || defined(_MIPSEB) || defined(_POWER) || defined(__s390__)
|
||||
# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN
|
||||
# elif defined(__i386__) || defined(__alpha__) || defined(__ia64) || defined(__ia64__) || defined(_M_IX86) || defined(_M_IA64) || defined(_M_ALPHA) || defined(__amd64) || defined(__amd64__) || defined(_M_AMD64) || defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || defined(__bfin__)
|
||||
# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN
|
||||
# elif defined(RAPIDJSON_DOXYGEN_RUNNING)
|
||||
# define RAPIDJSON_ENDIAN
|
||||
# else
|
||||
# error Unknown machine endianess detected. User needs to define RAPIDJSON_ENDIAN.
|
||||
# endif
|
||||
#endif // RAPIDJSON_ENDIAN
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// RAPIDJSON_64BIT
|
||||
|
||||
//! Whether using 64-bit architecture
|
||||
#ifndef RAPIDJSON_64BIT
|
||||
#if defined(__LP64__) || defined(_WIN64)
|
||||
#define RAPIDJSON_64BIT 1
|
||||
#else
|
||||
#define RAPIDJSON_64BIT 0
|
||||
#endif
|
||||
#endif // RAPIDJSON_64BIT
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// RAPIDJSON_ALIGN
|
||||
|
||||
//! Data alignment of the machine.
|
||||
/*! \ingroup RAPIDJSON_CONFIG
|
||||
\param x pointer to align
|
||||
|
||||
Some machines require strict data alignment. Currently the default uses 4 bytes
|
||||
alignment. User can customize by defining the RAPIDJSON_ALIGN function macro.,
|
||||
*/
|
||||
#ifndef RAPIDJSON_ALIGN
|
||||
#define RAPIDJSON_ALIGN(x) ((x + 3u) & ~3u)
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// RAPIDJSON_UINT64_C2
|
||||
|
||||
//! Construct a 64-bit literal by a pair of 32-bit integer.
|
||||
/*!
|
||||
64-bit literal with or without ULL suffix is prone to compiler warnings.
|
||||
UINT64_C() is C macro which cause compilation problems.
|
||||
Use this macro to define 64-bit constants by a pair of 32-bit integer.
|
||||
*/
|
||||
#ifndef RAPIDJSON_UINT64_C2
|
||||
#define RAPIDJSON_UINT64_C2(high32, low32) ((static_cast<uint64_t>(high32) << 32) | static_cast<uint64_t>(low32))
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// RAPIDJSON_SSE2/RAPIDJSON_SSE42/RAPIDJSON_SIMD
|
||||
|
||||
/*! \def RAPIDJSON_SIMD
|
||||
\ingroup RAPIDJSON_CONFIG
|
||||
\brief Enable SSE2/SSE4.2 optimization.
|
||||
|
||||
RapidJSON supports optimized implementations for some parsing operations
|
||||
based on the SSE2 or SSE4.2 SIMD extensions on modern Intel-compatible
|
||||
processors.
|
||||
|
||||
To enable these optimizations, two different symbols can be defined;
|
||||
\code
|
||||
// Enable SSE2 optimization.
|
||||
#define RAPIDJSON_SSE2
|
||||
|
||||
// Enable SSE4.2 optimization.
|
||||
#define RAPIDJSON_SSE42
|
||||
\endcode
|
||||
|
||||
\c RAPIDJSON_SSE42 takes precedence, if both are defined.
|
||||
|
||||
If any of these symbols is defined, RapidJSON defines the macro
|
||||
\c RAPIDJSON_SIMD to indicate the availability of the optimized code.
|
||||
*/
|
||||
#if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) \
|
||||
|| defined(RAPIDJSON_DOXYGEN_RUNNING)
|
||||
#define RAPIDJSON_SIMD
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// RAPIDJSON_NO_SIZETYPEDEFINE
|
||||
|
||||
#ifndef RAPIDJSON_NO_SIZETYPEDEFINE
|
||||
/*! \def RAPIDJSON_NO_SIZETYPEDEFINE
|
||||
\ingroup RAPIDJSON_CONFIG
|
||||
\brief User-provided \c SizeType definition.
|
||||
|
||||
In order to avoid using 32-bit size types for indexing strings and arrays,
|
||||
define this preprocessor symbol and provide the type rapidjson::SizeType
|
||||
before including RapidJSON:
|
||||
\code
|
||||
#define RAPIDJSON_NO_SIZETYPEDEFINE
|
||||
namespace rapidjson { typedef ::std::size_t SizeType; }
|
||||
#include "rapidjson/..."
|
||||
\endcode
|
||||
|
||||
\see rapidjson::SizeType
|
||||
*/
|
||||
#ifdef RAPIDJSON_DOXYGEN_RUNNING
|
||||
#define RAPIDJSON_NO_SIZETYPEDEFINE
|
||||
#endif
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
//! Size type (for string lengths, array sizes, etc.)
|
||||
/*! RapidJSON uses 32-bit array/string indices even on 64-bit platforms,
|
||||
instead of using \c size_t. Users may override the SizeType by defining
|
||||
\ref RAPIDJSON_NO_SIZETYPEDEFINE.
|
||||
*/
|
||||
typedef unsigned SizeType;
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
#endif
|
||||
|
||||
// always import std::size_t to rapidjson namespace
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
using std::size_t;
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// RAPIDJSON_ASSERT
|
||||
|
||||
//! Assertion.
|
||||
/*! \ingroup RAPIDJSON_CONFIG
|
||||
By default, rapidjson uses C \c assert() for internal assertions.
|
||||
User can override it by defining RAPIDJSON_ASSERT(x) macro.
|
||||
|
||||
\note Parsing errors are handled and can be customized by the
|
||||
\ref RAPIDJSON_ERRORS APIs.
|
||||
*/
|
||||
#ifndef RAPIDJSON_ASSERT
|
||||
#include <cassert>
|
||||
#define RAPIDJSON_ASSERT(x) assert(x)
|
||||
#endif // RAPIDJSON_ASSERT
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// RAPIDJSON_STATIC_ASSERT
|
||||
|
||||
// Adopt from boost
|
||||
#ifndef RAPIDJSON_STATIC_ASSERT
|
||||
//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
template <bool x> struct STATIC_ASSERTION_FAILURE;
|
||||
template <> struct STATIC_ASSERTION_FAILURE<true> { enum { value = 1 }; };
|
||||
template<int x> struct StaticAssertTest {};
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#define RAPIDJSON_JOIN(X, Y) RAPIDJSON_DO_JOIN(X, Y)
|
||||
#define RAPIDJSON_DO_JOIN(X, Y) RAPIDJSON_DO_JOIN2(X, Y)
|
||||
#define RAPIDJSON_DO_JOIN2(X, Y) X##Y
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#define RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE __attribute__((unused))
|
||||
#else
|
||||
#define RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE
|
||||
#endif
|
||||
//!@endcond
|
||||
|
||||
/*! \def RAPIDJSON_STATIC_ASSERT
|
||||
\brief (Internal) macro to check for conditions at compile-time
|
||||
\param x compile-time condition
|
||||
\hideinitializer
|
||||
*/
|
||||
#define RAPIDJSON_STATIC_ASSERT(x) \
|
||||
typedef ::RAPIDJSON_NAMESPACE::StaticAssertTest< \
|
||||
sizeof(::RAPIDJSON_NAMESPACE::STATIC_ASSERTION_FAILURE<bool(x) >)> \
|
||||
RAPIDJSON_JOIN(StaticAssertTypedef, __LINE__) RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Helpers
|
||||
|
||||
//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN
|
||||
|
||||
#define RAPIDJSON_MULTILINEMACRO_BEGIN do {
|
||||
#define RAPIDJSON_MULTILINEMACRO_END \
|
||||
} while((void)0, 0)
|
||||
|
||||
// adopted from Boost
|
||||
#define RAPIDJSON_VERSION_CODE(x,y,z) \
|
||||
(((x)*100000) + ((y)*100) + (z))
|
||||
|
||||
// token stringification
|
||||
#define RAPIDJSON_STRINGIFY(x) RAPIDJSON_DO_STRINGIFY(x)
|
||||
#define RAPIDJSON_DO_STRINGIFY(x) #x
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// RAPIDJSON_DIAG_PUSH/POP, RAPIDJSON_DIAG_OFF
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#define RAPIDJSON_GNUC \
|
||||
RAPIDJSON_VERSION_CODE(__GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__)
|
||||
#endif
|
||||
|
||||
#if defined(__clang__) || (defined(RAPIDJSON_GNUC) && RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,2,0))
|
||||
|
||||
#define RAPIDJSON_PRAGMA(x) _Pragma(RAPIDJSON_STRINGIFY(x))
|
||||
#define RAPIDJSON_DIAG_PRAGMA(x) RAPIDJSON_PRAGMA(GCC diagnostic x)
|
||||
#define RAPIDJSON_DIAG_OFF(x) \
|
||||
RAPIDJSON_DIAG_PRAGMA(ignored RAPIDJSON_STRINGIFY(RAPIDJSON_JOIN(-W,x)))
|
||||
|
||||
// push/pop support in Clang and GCC>=4.6
|
||||
#if defined(__clang__) || (defined(RAPIDJSON_GNUC) && RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0))
|
||||
#define RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_PRAGMA(push)
|
||||
#define RAPIDJSON_DIAG_POP RAPIDJSON_DIAG_PRAGMA(pop)
|
||||
#else // GCC >= 4.2, < 4.6
|
||||
#define RAPIDJSON_DIAG_PUSH /* ignored */
|
||||
#define RAPIDJSON_DIAG_POP /* ignored */
|
||||
#endif
|
||||
|
||||
#elif defined(_MSC_VER)
|
||||
|
||||
// pragma (MSVC specific)
|
||||
#define RAPIDJSON_PRAGMA(x) __pragma(x)
|
||||
#define RAPIDJSON_DIAG_PRAGMA(x) RAPIDJSON_PRAGMA(warning(x))
|
||||
|
||||
#define RAPIDJSON_DIAG_OFF(x) RAPIDJSON_DIAG_PRAGMA(disable: x)
|
||||
#define RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_PRAGMA(push)
|
||||
#define RAPIDJSON_DIAG_POP RAPIDJSON_DIAG_PRAGMA(pop)
|
||||
|
||||
#else
|
||||
|
||||
#define RAPIDJSON_DIAG_OFF(x) /* ignored */
|
||||
#define RAPIDJSON_DIAG_PUSH /* ignored */
|
||||
#define RAPIDJSON_DIAG_POP /* ignored */
|
||||
|
||||
#endif // RAPIDJSON_DIAG_*
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// C++11 features
|
||||
|
||||
#ifndef RAPIDJSON_HAS_CXX11_RVALUE_REFS
|
||||
#if defined(__clang__)
|
||||
#define RAPIDJSON_HAS_CXX11_RVALUE_REFS __has_feature(cxx_rvalue_references) && \
|
||||
(defined(_LIBCPP_VERSION) || defined(__GLIBCXX__) && __GLIBCXX__ >= 20080306)
|
||||
#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,3,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \
|
||||
(defined(_MSC_VER) && _MSC_VER >= 1600)
|
||||
|
||||
#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1
|
||||
#else
|
||||
#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 0
|
||||
#endif
|
||||
#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS
|
||||
|
||||
#ifndef RAPIDJSON_HAS_CXX11_NOEXCEPT
|
||||
#if defined(__clang__)
|
||||
#define RAPIDJSON_HAS_CXX11_NOEXCEPT __has_feature(cxx_noexcept)
|
||||
#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__))
|
||||
// (defined(_MSC_VER) && _MSC_VER >= ????) // not yet supported
|
||||
#define RAPIDJSON_HAS_CXX11_NOEXCEPT 1
|
||||
#else
|
||||
#define RAPIDJSON_HAS_CXX11_NOEXCEPT 0
|
||||
#endif
|
||||
#endif
|
||||
#if RAPIDJSON_HAS_CXX11_NOEXCEPT
|
||||
#define RAPIDJSON_NOEXCEPT noexcept
|
||||
#else
|
||||
#define RAPIDJSON_NOEXCEPT /* noexcept */
|
||||
#endif // RAPIDJSON_HAS_CXX11_NOEXCEPT
|
||||
|
||||
// no automatic detection, yet
|
||||
#ifndef RAPIDJSON_HAS_CXX11_TYPETRAITS
|
||||
#define RAPIDJSON_HAS_CXX11_TYPETRAITS 0
|
||||
#endif
|
||||
|
||||
//!@endcond
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// new/delete
|
||||
|
||||
#ifndef RAPIDJSON_NEW
|
||||
///! customization point for global \c new
|
||||
#define RAPIDJSON_NEW(x) new x
|
||||
#endif
|
||||
#ifndef RAPIDJSON_DELETE
|
||||
///! customization point for global \c delete
|
||||
#define RAPIDJSON_DELETE(x) delete x
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Allocators and Encodings
|
||||
|
||||
#include "allocators.h"
|
||||
#include "encodings.h"
|
||||
|
||||
/*! \namespace rapidjson
|
||||
\brief main RapidJSON namespace
|
||||
\see RAPIDJSON_NAMESPACE
|
||||
*/
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Stream
|
||||
|
||||
/*! \class rapidjson::Stream
|
||||
\brief Concept for reading and writing characters.
|
||||
|
||||
For read-only stream, no need to implement PutBegin(), Put(), Flush() and PutEnd().
|
||||
|
||||
For write-only stream, only need to implement Put() and Flush().
|
||||
|
||||
\code
|
||||
concept Stream {
|
||||
typename Ch; //!< Character type of the stream.
|
||||
|
||||
//! Read the current character from stream without moving the read cursor.
|
||||
Ch Peek() const;
|
||||
|
||||
//! Read the current character from stream and moving the read cursor to next character.
|
||||
Ch Take();
|
||||
|
||||
//! Get the current read cursor.
|
||||
//! \return Number of characters read from start.
|
||||
size_t Tell();
|
||||
|
||||
//! Begin writing operation at the current read pointer.
|
||||
//! \return The begin writer pointer.
|
||||
Ch* PutBegin();
|
||||
|
||||
//! Write a character.
|
||||
void Put(Ch c);
|
||||
|
||||
//! Flush the buffer.
|
||||
void Flush();
|
||||
|
||||
//! End the writing operation.
|
||||
//! \param begin The begin write pointer returned by PutBegin().
|
||||
//! \return Number of characters written.
|
||||
size_t PutEnd(Ch* begin);
|
||||
}
|
||||
\endcode
|
||||
*/
|
||||
|
||||
//! Provides additional information for stream.
|
||||
/*!
|
||||
By using traits pattern, this type provides a default configuration for stream.
|
||||
For custom stream, this type can be specialized for other configuration.
|
||||
See TEST(Reader, CustomStringStream) in readertest.cpp for example.
|
||||
*/
|
||||
template<typename Stream>
|
||||
struct StreamTraits {
|
||||
//! Whether to make local copy of stream for optimization during parsing.
|
||||
/*!
|
||||
By default, for safety, streams do not use local copy optimization.
|
||||
Stream that can be copied fast should specialize this, like StreamTraits<StringStream>.
|
||||
*/
|
||||
enum { copyOptimization = 0 };
|
||||
};
|
||||
|
||||
//! Put N copies of a character to a stream.
|
||||
template<typename Stream, typename Ch>
|
||||
inline void PutN(Stream& stream, Ch c, size_t n) {
|
||||
for (size_t i = 0; i < n; i++)
|
||||
stream.Put(c);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// StringStream
|
||||
|
||||
//! Read-only string stream.
|
||||
/*! \note implements Stream concept
|
||||
*/
|
||||
template <typename Encoding>
|
||||
struct GenericStringStream {
|
||||
typedef typename Encoding::Ch Ch;
|
||||
|
||||
GenericStringStream(const Ch *src) : src_(src), head_(src) {}
|
||||
|
||||
Ch Peek() const { return *src_; }
|
||||
Ch Take() { return *src_++; }
|
||||
size_t Tell() const { return static_cast<size_t>(src_ - head_); }
|
||||
|
||||
Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
|
||||
void Put(Ch) { RAPIDJSON_ASSERT(false); }
|
||||
void Flush() { RAPIDJSON_ASSERT(false); }
|
||||
size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
|
||||
|
||||
const Ch* src_; //!< Current read position.
|
||||
const Ch* head_; //!< Original head of the string.
|
||||
};
|
||||
|
||||
template <typename Encoding>
|
||||
struct StreamTraits<GenericStringStream<Encoding> > {
|
||||
enum { copyOptimization = 1 };
|
||||
};
|
||||
|
||||
//! String stream with UTF8 encoding.
|
||||
typedef GenericStringStream<UTF8<> > StringStream;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// InsituStringStream
|
||||
|
||||
//! A read-write string stream.
|
||||
/*! This string stream is particularly designed for in-situ parsing.
|
||||
\note implements Stream concept
|
||||
*/
|
||||
template <typename Encoding>
|
||||
struct GenericInsituStringStream {
|
||||
typedef typename Encoding::Ch Ch;
|
||||
|
||||
GenericInsituStringStream(Ch *src) : src_(src), dst_(0), head_(src) {}
|
||||
|
||||
// Read
|
||||
Ch Peek() { return *src_; }
|
||||
Ch Take() { return *src_++; }
|
||||
size_t Tell() { return static_cast<size_t>(src_ - head_); }
|
||||
|
||||
// Write
|
||||
void Put(Ch c) { RAPIDJSON_ASSERT(dst_ != 0); *dst_++ = c; }
|
||||
|
||||
Ch* PutBegin() { return dst_ = src_; }
|
||||
size_t PutEnd(Ch* begin) { return static_cast<size_t>(dst_ - begin); }
|
||||
void Flush() {}
|
||||
|
||||
Ch* Push(size_t count) { Ch* begin = dst_; dst_ += count; return begin; }
|
||||
void Pop(size_t count) { dst_ -= count; }
|
||||
|
||||
Ch* src_;
|
||||
Ch* dst_;
|
||||
Ch* head_;
|
||||
};
|
||||
|
||||
template <typename Encoding>
|
||||
struct StreamTraits<GenericInsituStringStream<Encoding> > {
|
||||
enum { copyOptimization = 1 };
|
||||
};
|
||||
|
||||
//! Insitu string stream with UTF8 encoding.
|
||||
typedef GenericInsituStringStream<UTF8<> > InsituStringStream;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Type
|
||||
|
||||
//! Type of JSON value
|
||||
enum Type {
|
||||
kNullType = 0, //!< null
|
||||
kFalseType = 1, //!< false
|
||||
kTrueType = 2, //!< true
|
||||
kObjectType = 3, //!< object
|
||||
kArrayType = 4, //!< array
|
||||
kStringType = 5, //!< string
|
||||
kNumberType = 6 //!< number
|
||||
};
|
||||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_RAPIDJSON_H_
|
||||
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
@@ -0,0 +1,93 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_STRINGBUFFER_H_
|
||||
#define RAPIDJSON_STRINGBUFFER_H_
|
||||
|
||||
#include "rapidjson.h"
|
||||
|
||||
#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
|
||||
#include <utility> // std::move
|
||||
#endif
|
||||
|
||||
#include "internal/stack.h"
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
|
||||
//! Represents an in-memory output stream.
|
||||
/*!
|
||||
\tparam Encoding Encoding of the stream.
|
||||
\tparam Allocator type for allocating memory buffer.
|
||||
\note implements Stream concept
|
||||
*/
|
||||
template <typename Encoding, typename Allocator = CrtAllocator>
|
||||
class GenericStringBuffer {
|
||||
public:
|
||||
typedef typename Encoding::Ch Ch;
|
||||
|
||||
GenericStringBuffer(Allocator* allocator = 0, size_t capacity = kDefaultCapacity) : stack_(allocator, capacity) {}
|
||||
|
||||
#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
|
||||
GenericStringBuffer(GenericStringBuffer&& rhs) : stack_(std::move(rhs.stack_)) {}
|
||||
GenericStringBuffer& operator=(GenericStringBuffer&& rhs) {
|
||||
if (&rhs != this)
|
||||
stack_ = std::move(rhs.stack_);
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
||||
void Put(Ch c) { *stack_.template Push<Ch>() = c; }
|
||||
void Flush() {}
|
||||
|
||||
void Clear() { stack_.Clear(); }
|
||||
void ShrinkToFit() {
|
||||
// Push and pop a null terminator. This is safe.
|
||||
*stack_.template Push<Ch>() = '\0';
|
||||
stack_.ShrinkToFit();
|
||||
stack_.template Pop<Ch>(1);
|
||||
}
|
||||
Ch* Push(size_t count) { return stack_.template Push<Ch>(count); }
|
||||
void Pop(size_t count) { stack_.template Pop<Ch>(count); }
|
||||
|
||||
const Ch* GetString() const {
|
||||
// Push and pop a null terminator. This is safe.
|
||||
*stack_.template Push<Ch>() = '\0';
|
||||
stack_.template Pop<Ch>(1);
|
||||
|
||||
return stack_.template Bottom<Ch>();
|
||||
}
|
||||
|
||||
size_t GetSize() const { return stack_.GetSize(); }
|
||||
|
||||
static const size_t kDefaultCapacity = 256;
|
||||
mutable internal::Stack<Allocator> stack_;
|
||||
|
||||
private:
|
||||
// Prohibit copy constructor & assignment operator.
|
||||
GenericStringBuffer(const GenericStringBuffer&);
|
||||
GenericStringBuffer& operator=(const GenericStringBuffer&);
|
||||
};
|
||||
|
||||
//! String buffer with UTF8 encoding
|
||||
typedef GenericStringBuffer<UTF8<> > StringBuffer;
|
||||
|
||||
//! Implement specialized version of PutN() with memset() for better performance.
|
||||
template<>
|
||||
inline void PutN(GenericStringBuffer<UTF8<> >& stream, char c, size_t n) {
|
||||
std::memset(stream.stack_.Push<char>(n), c, n * sizeof(c));
|
||||
}
|
||||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_STRINGBUFFER_H_
|
||||
@@ -0,0 +1,395 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_WRITER_H_
|
||||
#define RAPIDJSON_WRITER_H_
|
||||
|
||||
#include "rapidjson.h"
|
||||
#include "internal/stack.h"
|
||||
#include "internal/strfunc.h"
|
||||
#include "internal/dtoa.h"
|
||||
#include "internal/itoa.h"
|
||||
#include "stringbuffer.h"
|
||||
#include <new> // placement new
|
||||
|
||||
#if RAPIDJSON_HAS_STDSTRING
|
||||
#include <string>
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant
|
||||
#endif
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
|
||||
//! JSON writer
|
||||
/*! Writer implements the concept Handler.
|
||||
It generates JSON text by events to an output os.
|
||||
|
||||
User may programmatically calls the functions of a writer to generate JSON text.
|
||||
|
||||
On the other side, a writer can also be passed to objects that generates events,
|
||||
|
||||
for example Reader::Parse() and Document::Accept().
|
||||
|
||||
\tparam OutputStream Type of output stream.
|
||||
\tparam SourceEncoding Encoding of source string.
|
||||
\tparam TargetEncoding Encoding of output stream.
|
||||
\tparam StackAllocator Type of allocator for allocating memory of stack.
|
||||
\note implements Handler concept
|
||||
*/
|
||||
template<typename OutputStream, typename SourceEncoding = UTF8<>, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator>
|
||||
class Writer {
|
||||
public:
|
||||
typedef typename SourceEncoding::Ch Ch;
|
||||
|
||||
//! Constructor
|
||||
/*! \param os Output stream.
|
||||
\param stackAllocator User supplied allocator. If it is null, it will create a private one.
|
||||
\param levelDepth Initial capacity of stack.
|
||||
*/
|
||||
explicit
|
||||
Writer(OutputStream& os, StackAllocator* stackAllocator = 0, size_t levelDepth = kDefaultLevelDepth) :
|
||||
os_(&os), level_stack_(stackAllocator, levelDepth * sizeof(Level)), hasRoot_(false) {}
|
||||
|
||||
explicit
|
||||
Writer(StackAllocator* allocator = 0, size_t levelDepth = kDefaultLevelDepth) :
|
||||
os_(0), level_stack_(allocator, levelDepth * sizeof(Level)), hasRoot_(false) {}
|
||||
|
||||
//! Reset the writer with a new stream.
|
||||
/*!
|
||||
This function reset the writer with a new stream and default settings,
|
||||
in order to make a Writer object reusable for output multiple JSONs.
|
||||
|
||||
\param os New output stream.
|
||||
\code
|
||||
Writer<OutputStream> writer(os1);
|
||||
writer.StartObject();
|
||||
// ...
|
||||
writer.EndObject();
|
||||
|
||||
writer.Reset(os2);
|
||||
writer.StartObject();
|
||||
// ...
|
||||
writer.EndObject();
|
||||
\endcode
|
||||
*/
|
||||
void Reset(OutputStream& os) {
|
||||
os_ = &os;
|
||||
hasRoot_ = false;
|
||||
level_stack_.Clear();
|
||||
}
|
||||
|
||||
//! Checks whether the output is a complete JSON.
|
||||
/*!
|
||||
A complete JSON has a complete root object or array.
|
||||
*/
|
||||
bool IsComplete() const {
|
||||
return hasRoot_ && level_stack_.Empty();
|
||||
}
|
||||
|
||||
/*!@name Implementation of Handler
|
||||
\see Handler
|
||||
*/
|
||||
//@{
|
||||
|
||||
bool Null() { Prefix(kNullType); return WriteNull(); }
|
||||
bool Bool(bool b) { Prefix(b ? kTrueType : kFalseType); return WriteBool(b); }
|
||||
bool Int(int i) { Prefix(kNumberType); return WriteInt(i); }
|
||||
bool Uint(unsigned u) { Prefix(kNumberType); return WriteUint(u); }
|
||||
bool Int64(int64_t i64) { Prefix(kNumberType); return WriteInt64(i64); }
|
||||
bool Uint64(uint64_t u64) { Prefix(kNumberType); return WriteUint64(u64); }
|
||||
|
||||
//! Writes the given \c double value to the stream
|
||||
/*!
|
||||
\param d The value to be written.
|
||||
\return Whether it is succeed.
|
||||
*/
|
||||
bool Double(double d) { Prefix(kNumberType); return WriteDouble(d); }
|
||||
|
||||
bool String(const Ch* str, SizeType length, bool copy = false) {
|
||||
(void)copy;
|
||||
Prefix(kStringType);
|
||||
return WriteString(str, length);
|
||||
}
|
||||
|
||||
#if RAPIDJSON_HAS_STDSTRING
|
||||
bool String(const std::basic_string<Ch>& str) {
|
||||
return String(str.data(), SizeType(str.size()));
|
||||
}
|
||||
#endif
|
||||
|
||||
bool StartObject() {
|
||||
Prefix(kObjectType);
|
||||
new (level_stack_.template Push<Level>()) Level(false);
|
||||
return WriteStartObject();
|
||||
}
|
||||
|
||||
bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); }
|
||||
|
||||
bool EndObject(SizeType memberCount = 0) {
|
||||
(void)memberCount;
|
||||
RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level));
|
||||
RAPIDJSON_ASSERT(!level_stack_.template Top<Level>()->inArray);
|
||||
level_stack_.template Pop<Level>(1);
|
||||
bool ret = WriteEndObject();
|
||||
if (level_stack_.Empty()) // end of json text
|
||||
os_->Flush();
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool StartArray() {
|
||||
Prefix(kArrayType);
|
||||
new (level_stack_.template Push<Level>()) Level(true);
|
||||
return WriteStartArray();
|
||||
}
|
||||
|
||||
bool EndArray(SizeType elementCount = 0) {
|
||||
(void)elementCount;
|
||||
RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level));
|
||||
RAPIDJSON_ASSERT(level_stack_.template Top<Level>()->inArray);
|
||||
level_stack_.template Pop<Level>(1);
|
||||
bool ret = WriteEndArray();
|
||||
if (level_stack_.Empty()) // end of json text
|
||||
os_->Flush();
|
||||
return ret;
|
||||
}
|
||||
//@}
|
||||
|
||||
/*! @name Convenience extensions */
|
||||
//@{
|
||||
|
||||
//! Simpler but slower overload.
|
||||
bool String(const Ch* str) { return String(str, internal::StrLen(str)); }
|
||||
bool Key(const Ch* str) { return Key(str, internal::StrLen(str)); }
|
||||
|
||||
//@}
|
||||
|
||||
protected:
|
||||
//! Information for each nested level
|
||||
struct Level {
|
||||
Level(bool inArray_) : valueCount(0), inArray(inArray_) {}
|
||||
size_t valueCount; //!< number of values in this level
|
||||
bool inArray; //!< true if in array, otherwise in object
|
||||
};
|
||||
|
||||
static const size_t kDefaultLevelDepth = 32;
|
||||
|
||||
bool WriteNull() {
|
||||
os_->Put('n'); os_->Put('u'); os_->Put('l'); os_->Put('l'); return true;
|
||||
}
|
||||
|
||||
bool WriteBool(bool b) {
|
||||
if (b) {
|
||||
os_->Put('t'); os_->Put('r'); os_->Put('u'); os_->Put('e');
|
||||
}
|
||||
else {
|
||||
os_->Put('f'); os_->Put('a'); os_->Put('l'); os_->Put('s'); os_->Put('e');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteInt(int i) {
|
||||
char buffer[11];
|
||||
const char* end = internal::i32toa(i, buffer);
|
||||
for (const char* p = buffer; p != end; ++p)
|
||||
os_->Put(*p);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteUint(unsigned u) {
|
||||
char buffer[10];
|
||||
const char* end = internal::u32toa(u, buffer);
|
||||
for (const char* p = buffer; p != end; ++p)
|
||||
os_->Put(*p);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteInt64(int64_t i64) {
|
||||
char buffer[21];
|
||||
const char* end = internal::i64toa(i64, buffer);
|
||||
for (const char* p = buffer; p != end; ++p)
|
||||
os_->Put(*p);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteUint64(uint64_t u64) {
|
||||
char buffer[20];
|
||||
char* end = internal::u64toa(u64, buffer);
|
||||
for (char* p = buffer; p != end; ++p)
|
||||
os_->Put(*p);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteDouble(double d) {
|
||||
char buffer[25];
|
||||
char* end = internal::dtoa(d, buffer);
|
||||
for (char* p = buffer; p != end; ++p)
|
||||
os_->Put(*p);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteString(const Ch* str, SizeType length) {
|
||||
static const char hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
|
||||
static const char escape[256] = {
|
||||
#define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
||||
//0 1 2 3 4 5 6 7 8 9 A B C D E F
|
||||
'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'b', 't', 'n', 'u', 'f', 'r', 'u', 'u', // 00
|
||||
'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', // 10
|
||||
0, 0, '"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20
|
||||
Z16, Z16, // 30~4F
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0, // 50
|
||||
Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16 // 60~FF
|
||||
#undef Z16
|
||||
};
|
||||
|
||||
os_->Put('\"');
|
||||
GenericStringStream<SourceEncoding> is(str);
|
||||
while (is.Tell() < length) {
|
||||
const Ch c = is.Peek();
|
||||
if (!TargetEncoding::supportUnicode && (unsigned)c >= 0x80) {
|
||||
// Unicode escaping
|
||||
unsigned codepoint;
|
||||
if (!SourceEncoding::Decode(is, &codepoint))
|
||||
return false;
|
||||
os_->Put('\\');
|
||||
os_->Put('u');
|
||||
if (codepoint <= 0xD7FF || (codepoint >= 0xE000 && codepoint <= 0xFFFF)) {
|
||||
os_->Put(hexDigits[(codepoint >> 12) & 15]);
|
||||
os_->Put(hexDigits[(codepoint >> 8) & 15]);
|
||||
os_->Put(hexDigits[(codepoint >> 4) & 15]);
|
||||
os_->Put(hexDigits[(codepoint ) & 15]);
|
||||
}
|
||||
else {
|
||||
RAPIDJSON_ASSERT(codepoint >= 0x010000 && codepoint <= 0x10FFFF);
|
||||
// Surrogate pair
|
||||
unsigned s = codepoint - 0x010000;
|
||||
unsigned lead = (s >> 10) + 0xD800;
|
||||
unsigned trail = (s & 0x3FF) + 0xDC00;
|
||||
os_->Put(hexDigits[(lead >> 12) & 15]);
|
||||
os_->Put(hexDigits[(lead >> 8) & 15]);
|
||||
os_->Put(hexDigits[(lead >> 4) & 15]);
|
||||
os_->Put(hexDigits[(lead ) & 15]);
|
||||
os_->Put('\\');
|
||||
os_->Put('u');
|
||||
os_->Put(hexDigits[(trail >> 12) & 15]);
|
||||
os_->Put(hexDigits[(trail >> 8) & 15]);
|
||||
os_->Put(hexDigits[(trail >> 4) & 15]);
|
||||
os_->Put(hexDigits[(trail ) & 15]);
|
||||
}
|
||||
}
|
||||
else if ((sizeof(Ch) == 1 || (unsigned)c < 256) && escape[(unsigned char)c]) {
|
||||
is.Take();
|
||||
os_->Put('\\');
|
||||
os_->Put(escape[(unsigned char)c]);
|
||||
if (escape[(unsigned char)c] == 'u') {
|
||||
os_->Put('0');
|
||||
os_->Put('0');
|
||||
os_->Put(hexDigits[(unsigned char)c >> 4]);
|
||||
os_->Put(hexDigits[(unsigned char)c & 0xF]);
|
||||
}
|
||||
}
|
||||
else
|
||||
if (!Transcoder<SourceEncoding, TargetEncoding>::Transcode(is, *os_))
|
||||
return false;
|
||||
}
|
||||
os_->Put('\"');
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteStartObject() { os_->Put('{'); return true; }
|
||||
bool WriteEndObject() { os_->Put('}'); return true; }
|
||||
bool WriteStartArray() { os_->Put('['); return true; }
|
||||
bool WriteEndArray() { os_->Put(']'); return true; }
|
||||
|
||||
void Prefix(Type type) {
|
||||
(void)type;
|
||||
if (level_stack_.GetSize() != 0) { // this value is not at root
|
||||
Level* level = level_stack_.template Top<Level>();
|
||||
if (level->valueCount > 0) {
|
||||
if (level->inArray)
|
||||
os_->Put(','); // add comma if it is not the first element in array
|
||||
else // in object
|
||||
os_->Put((level->valueCount % 2 == 0) ? ',' : ':');
|
||||
}
|
||||
if (!level->inArray && level->valueCount % 2 == 0)
|
||||
RAPIDJSON_ASSERT(type == kStringType); // if it's in object, then even number should be a name
|
||||
level->valueCount++;
|
||||
}
|
||||
else {
|
||||
RAPIDJSON_ASSERT(!hasRoot_); // Should only has one and only one root.
|
||||
hasRoot_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
OutputStream* os_;
|
||||
internal::Stack<StackAllocator> level_stack_;
|
||||
bool hasRoot_;
|
||||
|
||||
private:
|
||||
// Prohibit copy constructor & assignment operator.
|
||||
Writer(const Writer&);
|
||||
Writer& operator=(const Writer&);
|
||||
};
|
||||
|
||||
// Full specialization for StringStream to prevent memory copying
|
||||
|
||||
template<>
|
||||
inline bool Writer<StringBuffer>::WriteInt(int i) {
|
||||
char *buffer = os_->Push(11);
|
||||
const char* end = internal::i32toa(i, buffer);
|
||||
os_->Pop(11 - (end - buffer));
|
||||
return true;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline bool Writer<StringBuffer>::WriteUint(unsigned u) {
|
||||
char *buffer = os_->Push(10);
|
||||
const char* end = internal::u32toa(u, buffer);
|
||||
os_->Pop(10 - (end - buffer));
|
||||
return true;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline bool Writer<StringBuffer>::WriteInt64(int64_t i64) {
|
||||
char *buffer = os_->Push(21);
|
||||
const char* end = internal::i64toa(i64, buffer);
|
||||
os_->Pop(21 - (end - buffer));
|
||||
return true;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline bool Writer<StringBuffer>::WriteUint64(uint64_t u) {
|
||||
char *buffer = os_->Push(20);
|
||||
const char* end = internal::u64toa(u, buffer);
|
||||
os_->Pop(20 - (end - buffer));
|
||||
return true;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline bool Writer<StringBuffer>::WriteDouble(double d) {
|
||||
char *buffer = os_->Push(25);
|
||||
char* end = internal::dtoa(d, buffer);
|
||||
os_->Pop(25 - (end - buffer));
|
||||
return true;
|
||||
}
|
||||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#ifdef _MSC_VER
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
#endif // RAPIDJSON_RAPIDJSON_H_
|
||||
@@ -0,0 +1,473 @@
|
||||
#include "Weaver.h"
|
||||
|
||||
#include <cmath> // sqrt
|
||||
#include <fstream> // debug IO
|
||||
#include <unistd.h>
|
||||
|
||||
#include "weaveDataStorage.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
void Weaver::weave(PrintObject* object, CommandSocket* commandSocket)
|
||||
{
|
||||
int maxz = object->max().z;
|
||||
|
||||
int layer_count = (maxz - initial_layer_thickness) / connectionHeight + 1;
|
||||
|
||||
DEBUG_SHOW(layer_count);
|
||||
|
||||
std::vector<cura::Slicer*> slicerList;
|
||||
|
||||
for(Mesh& mesh : object->meshes)
|
||||
{
|
||||
cura::Slicer* slicer = new cura::Slicer(&mesh, initial_layer_thickness, connectionHeight, layer_count, mesh.getSettingBoolean("meshfix_keep_open_polygons"), mesh.getSettingBoolean("meshfix_extensive_stitching"));
|
||||
slicerList.push_back(slicer);
|
||||
}
|
||||
|
||||
|
||||
int starting_layer_idx;
|
||||
{ // find first non-empty layer
|
||||
for (starting_layer_idx = 0; starting_layer_idx < layer_count; starting_layer_idx++)
|
||||
{
|
||||
Polygons parts;
|
||||
for (cura::Slicer* slicer : slicerList)
|
||||
parts.add(slicer->layers[starting_layer_idx].polygonList);
|
||||
|
||||
if (parts.size() > 0)
|
||||
break;
|
||||
}
|
||||
if (starting_layer_idx > 0)
|
||||
{
|
||||
logError("First %i layers are empty!\n", starting_layer_idx);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::cerr<< "chainifying layers..." << std::endl;
|
||||
{
|
||||
int starting_z = -1;
|
||||
for (cura::Slicer* slicer : slicerList)
|
||||
wireFrame.bottom_outline.add(slicer->layers[starting_layer_idx].polygonList);
|
||||
|
||||
if (commandSocket)
|
||||
commandSocket->sendPolygons(Inset0Type, 0, wireFrame.bottom_outline, 1);
|
||||
|
||||
wireFrame.z_bottom = slicerList[0]->layers[starting_layer_idx].z;
|
||||
|
||||
Point starting_point_in_layer;
|
||||
if (wireFrame.bottom_outline.size() > 0)
|
||||
starting_point_in_layer = (wireFrame.bottom_outline.max() + wireFrame.bottom_outline.min()) / 2;
|
||||
else
|
||||
starting_point_in_layer = (Point(0,0) + object->max() + object->min()) / 2;
|
||||
|
||||
for (int layer_idx = starting_layer_idx + 1; layer_idx < layer_count; layer_idx++)
|
||||
{
|
||||
logProgress("inset", layer_idx+1, layer_count); // abuse the progress system of the normal mode of CuraEngine
|
||||
|
||||
Polygons parts1;
|
||||
for (cura::Slicer* slicer : slicerList)
|
||||
parts1.add(slicer->layers[layer_idx].polygonList);
|
||||
|
||||
|
||||
Polygons chainified;
|
||||
|
||||
chainify_polygons(parts1, starting_point_in_layer, chainified, false);
|
||||
|
||||
if (commandSocket)
|
||||
commandSocket->sendPolygons(Inset0Type, layer_idx - starting_layer_idx, chainified, 1);
|
||||
|
||||
if (chainified.size() > 0)
|
||||
{
|
||||
if (starting_z == -1) starting_z = slicerList[0]->layers[layer_idx-1].z;
|
||||
wireFrame.layers.emplace_back();
|
||||
WeaveLayer& layer = wireFrame.layers.back();
|
||||
|
||||
layer.z0 = slicerList[0]->layers[layer_idx-1].z - starting_z;
|
||||
layer.z1 = slicerList[0]->layers[layer_idx].z - starting_z;
|
||||
|
||||
layer.supported = chainified;
|
||||
|
||||
starting_point_in_layer = layer.supported.back().back();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::cerr<< "finding horizontal parts..." << std::endl;
|
||||
{
|
||||
Polygons* lower_top_parts = &wireFrame.bottom_outline;
|
||||
|
||||
for (unsigned int layer_idx = 0; layer_idx < wireFrame.layers.size(); layer_idx++)
|
||||
{
|
||||
logProgress("skin", layer_idx+1, wireFrame.layers.size()); // abuse the progress system of the normal mode of CuraEngine
|
||||
|
||||
WeaveLayer& layer = wireFrame.layers[layer_idx];
|
||||
|
||||
Polygons empty;
|
||||
Polygons& layer_above = (layer_idx+1 < wireFrame.layers.size())? wireFrame.layers[layer_idx+1].supported : empty;
|
||||
|
||||
createHorizontalFill(*lower_top_parts, layer, layer_above, layer.z1);
|
||||
lower_top_parts = &layer.supported;
|
||||
}
|
||||
}
|
||||
// at this point layer.supported still only contains the polygons to be connected
|
||||
// when connecting layers, we further add the supporting polygons created by the roofs
|
||||
|
||||
std::cerr<< "connecting layers..." << std::endl;
|
||||
{
|
||||
Polygons* lower_top_parts = &wireFrame.bottom_outline;
|
||||
int last_z = wireFrame.z_bottom;
|
||||
for (unsigned int layer_idx = 0; layer_idx < wireFrame.layers.size(); layer_idx++) // use top of every layer but the last
|
||||
{
|
||||
WeaveLayer& layer = wireFrame.layers[layer_idx];
|
||||
|
||||
connect_polygons(*lower_top_parts, last_z, layer.supported, layer.z1, layer);
|
||||
layer.supported.add(layer.roofs.roof_outlines);
|
||||
lower_top_parts = &layer.supported;
|
||||
|
||||
last_z = layer.z1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
{ // roofs:
|
||||
|
||||
WeaveLayer& top_layer = wireFrame.layers.back();
|
||||
Polygons to_be_supported; // empty for the top layer
|
||||
fillRoofs(top_layer.supported, to_be_supported, -1, top_layer.z1, top_layer.roofs);
|
||||
}
|
||||
|
||||
|
||||
{ // bottom:
|
||||
Polygons to_be_supported; // is empty for the bottom layer, cause the order of insets doesn't really matter (in a sense everything is to be supported)
|
||||
fillRoofs(wireFrame.bottom_outline, to_be_supported, -1, wireFrame.layers.front().z0, wireFrame.bottom_infill);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Weaver::createHorizontalFill(Polygons& lower_top_parts, WeaveLayer& layer, Polygons& layer_above, int z1)
|
||||
{
|
||||
int64_t bridgable_dist = connectionHeight;
|
||||
|
||||
Polygons& polys_below = lower_top_parts;
|
||||
Polygons& polys_here = layer.supported;
|
||||
Polygons& polys_above = layer_above;
|
||||
|
||||
|
||||
{ // roofs
|
||||
Polygons to_be_supported = polys_above.offset(bridgable_dist);
|
||||
fillRoofs(polys_here, to_be_supported, -1, layer.z1, layer.roofs);
|
||||
|
||||
}
|
||||
|
||||
{ // floors
|
||||
Polygons to_be_supported = polys_above.offset(-bridgable_dist);
|
||||
fillFloors(polys_here, to_be_supported, 1, layer.z1, layer.roofs);
|
||||
}
|
||||
|
||||
{// optimize away doubly printed regions (boundaries of holes in layer etc.)
|
||||
for (WeaveRoofPart& inset : layer.roofs.roof_insets)
|
||||
connections2moves(inset);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void Weaver::fillRoofs(Polygons& supporting, Polygons& to_be_supported, int direction, int z, WeaveRoof& horizontals)
|
||||
{
|
||||
std::vector<WeaveRoofPart>& insets = horizontals.roof_insets;
|
||||
|
||||
if (supporting.size() == 0) return; // no parts to start the roof from!
|
||||
|
||||
Polygons roofs = supporting.difference(to_be_supported);
|
||||
|
||||
roofs = roofs.offset(-roof_inset).offset(roof_inset);
|
||||
|
||||
if (roofs.size() == 0) return;
|
||||
|
||||
|
||||
Polygons roof_outlines;
|
||||
Polygons roof_holes;
|
||||
{ // split roofs into outlines and holes
|
||||
std::vector<Polygons> roof_parts = roofs.splitIntoParts();
|
||||
for (Polygons& roof_part : roof_parts)
|
||||
{
|
||||
roof_outlines.add(roof_part[0]);
|
||||
for (unsigned int hole_idx = 1; hole_idx < roof_part.size(); hole_idx++)
|
||||
{
|
||||
roof_holes.add(roof_part[hole_idx]);
|
||||
roof_holes.back().reverse();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Polygons supporting_outlines;
|
||||
|
||||
std::vector<Polygons> supporting_parts = supporting.splitIntoParts();
|
||||
for (Polygons& supporting_part : supporting_parts)
|
||||
supporting_outlines.add(supporting_part[0]); // only add outlines, not the holes
|
||||
|
||||
|
||||
|
||||
Polygons inset1;
|
||||
Polygons last_inset;
|
||||
Polygons last_supported = supporting;
|
||||
for (Polygons inset0 = supporting_outlines; inset0.size() > 0; inset0 = last_inset)
|
||||
{
|
||||
last_inset = inset0.offset(direction * roof_inset, ClipperLib::jtRound);
|
||||
inset1 = last_inset.intersection(roof_outlines); // stay within roof area
|
||||
inset1 = inset1.unionPolygons(roof_holes);// make insets go around holes
|
||||
|
||||
if (inset1.size() == 0) break;
|
||||
|
||||
insets.emplace_back();
|
||||
|
||||
connect(last_supported, z, inset1, z, insets.back(), true);
|
||||
|
||||
inset1 = inset1.remove(roof_holes); // throw away holes which appear in every intersection
|
||||
inset1 = inset1.remove(roof_outlines);// throw away fully filled regions
|
||||
|
||||
last_supported = insets.back().supported; // chainified
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
horizontals.roof_outlines.add(roofs); // TODO just add the new lines, not the lines of the roofs which are already supported ==> make outlines into a connection from which we only print the top, not the connection
|
||||
}
|
||||
|
||||
void Weaver::fillFloors(Polygons& supporting, Polygons& to_be_supported, int direction, int z, WeaveRoof& horizontals)
|
||||
{
|
||||
std::vector<WeaveRoofPart>& outsets = horizontals.roof_insets;
|
||||
|
||||
if (to_be_supported.size() == 0) return; // no parts to start the floor from!
|
||||
if (supporting.size() == 0) return; // no parts to start the floor from!
|
||||
|
||||
Polygons floors = to_be_supported.difference(supporting);
|
||||
|
||||
floors = floors.offset(-roof_inset).offset(roof_inset);
|
||||
|
||||
if (floors.size() == 0) return;
|
||||
|
||||
|
||||
std::vector<Polygons> floor_parts = floors.splitIntoParts();
|
||||
|
||||
Polygons floor_outlines;
|
||||
Polygons floor_holes;
|
||||
for (Polygons& floor_part : floor_parts)
|
||||
{
|
||||
floor_outlines.add(floor_part[0]);
|
||||
for (unsigned int hole_idx = 1; hole_idx < floor_part.size(); hole_idx++)
|
||||
{
|
||||
floor_holes.add(floor_part[hole_idx]);
|
||||
//floor_holes.back().reverse();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Polygons outset1;
|
||||
|
||||
Polygons last_supported = supporting;
|
||||
|
||||
for (Polygons outset0 = supporting; outset0.size() > 0; outset0 = outset1)
|
||||
{
|
||||
outset1 = outset0.offset(roof_inset * direction, ClipperLib::jtRound).intersection(floors);
|
||||
outset1 = outset1.remove(floor_holes); // throw away holes which appear in every intersection
|
||||
outset1 = outset1.remove(floor_outlines); // throw away holes which appear in every intersection
|
||||
|
||||
|
||||
outsets.emplace_back();
|
||||
|
||||
connect(last_supported, z, outset1, z, outsets.back(), true);
|
||||
|
||||
outset1 = outset1.remove(floor_outlines);// throw away fully filled regions
|
||||
|
||||
last_supported = outsets.back().supported; // chainified
|
||||
|
||||
}
|
||||
|
||||
|
||||
horizontals.roof_outlines.add(floors);
|
||||
}
|
||||
|
||||
|
||||
void Weaver::connections2moves(WeaveRoofPart& inset)
|
||||
{
|
||||
|
||||
|
||||
bool include_half_of_last_down = true;
|
||||
|
||||
|
||||
for (WeaveConnectionPart& part : inset.connections)
|
||||
{
|
||||
std::vector<WeaveConnectionSegment>& segments = part.connection.segments;
|
||||
for (unsigned int idx = 0; idx < part.connection.segments.size(); idx += 2)
|
||||
{
|
||||
WeaveConnectionSegment& segment = segments[idx];
|
||||
assert(segment.segmentType == WeaveSegmentType::UP);
|
||||
Point3 from = (idx == 0)? part.connection.from : segments[idx-1].to;
|
||||
bool skipped = (segment.to - from).vSize2() < extrusionWidth * extrusionWidth;
|
||||
if (skipped)
|
||||
{
|
||||
unsigned int begin = idx;
|
||||
for (; idx < segments.size(); idx += 2)
|
||||
{
|
||||
WeaveConnectionSegment& segment = segments[idx];
|
||||
assert(segments[idx].segmentType == WeaveSegmentType::UP);
|
||||
Point3 from = (idx == 0)? part.connection.from : segments[idx-1].to;
|
||||
bool skipped = (segment.to - from).vSize2() < extrusionWidth * extrusionWidth;
|
||||
if (!skipped)
|
||||
break;
|
||||
}
|
||||
int end = idx - ((include_half_of_last_down)? 2 : 1);
|
||||
if (idx >= segments.size())
|
||||
segments.erase(segments.begin() + begin, segments.end());
|
||||
else
|
||||
{
|
||||
segments.erase(segments.begin() + begin, segments.begin() + end);
|
||||
if (begin < segments.size())
|
||||
{
|
||||
segments[begin].segmentType = WeaveSegmentType::MOVE;
|
||||
if (include_half_of_last_down)
|
||||
segments[begin+1].segmentType = WeaveSegmentType::DOWN_AND_FLAT;
|
||||
}
|
||||
idx = begin + ((include_half_of_last_down)? 2 : 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Weaver::connect(Polygons& parts0, int z0, Polygons& parts1, int z1, WeaveConnection& result, bool include_last)
|
||||
{
|
||||
// TODO: convert polygons (with outset + difference) such that after printing the first polygon, we can't be in the way of the printed stuff
|
||||
// something like:
|
||||
// for (m > n)
|
||||
// parts[m] = parts[m].difference(parts[n].offset(nozzle_top_diameter))
|
||||
// according to the printing order!
|
||||
//
|
||||
// OR! :
|
||||
//
|
||||
// unify different parts if gap is too small
|
||||
|
||||
Polygons& supported = result.supported;
|
||||
|
||||
if (parts1.size() == 0) return;
|
||||
|
||||
Point& start_close_to = (parts0.size() > 0)? parts0.back().back() : parts1.back().back();
|
||||
|
||||
chainify_polygons(parts1, start_close_to, supported, include_last);
|
||||
|
||||
if (parts0.size() == 0) return;
|
||||
|
||||
connect_polygons(parts0, z0, supported, z1, result);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void Weaver::chainify_polygons(Polygons& parts1, Point start_close_to, Polygons& result, bool include_last)
|
||||
{
|
||||
|
||||
|
||||
for (unsigned int prt = 0 ; prt < parts1.size(); prt++)
|
||||
{
|
||||
const PolygonRef upperPart = parts1[prt];
|
||||
|
||||
ClosestPolygonPoint closestInPoly = findClosest(start_close_to, upperPart);
|
||||
|
||||
|
||||
PolygonRef part_top = result.newPoly();
|
||||
|
||||
GivenDistPoint next_upper;
|
||||
bool found = true;
|
||||
int idx = 0;
|
||||
|
||||
for (Point upper_point = upperPart[closestInPoly.pos]; found; upper_point = next_upper.location)
|
||||
{
|
||||
found = getNextPointWithDistance(upper_point, nozzle_top_diameter, upperPart, idx, closestInPoly.pos, next_upper);
|
||||
|
||||
|
||||
if (!found)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
part_top.add(upper_point);
|
||||
|
||||
idx = next_upper.pos;
|
||||
}
|
||||
if (part_top.size() > 0)
|
||||
start_close_to = part_top.back();
|
||||
else
|
||||
result.remove(result.size()-1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Weaver::connect_polygons(Polygons& supporting, int z0, Polygons& supported, int z1, WeaveConnection& result)
|
||||
{
|
||||
|
||||
if (supporting.size() < 1)
|
||||
{
|
||||
DEBUG_PRINTLN("lower layer has zero parts!");
|
||||
return;
|
||||
}
|
||||
|
||||
result.z0 = z0;
|
||||
result.z1 = z1;
|
||||
|
||||
std::vector<WeaveConnectionPart>& parts = result.connections;
|
||||
|
||||
for (unsigned int prt = 0 ; prt < supported.size(); prt++)
|
||||
{
|
||||
|
||||
const PolygonRef upperPart = supported[prt];
|
||||
|
||||
|
||||
parts.emplace_back(prt);
|
||||
WeaveConnectionPart& part = parts.back();
|
||||
PolyLine3& connection = part.connection;
|
||||
|
||||
Point3 last_upper;
|
||||
bool firstIter = true;
|
||||
|
||||
for (const Point& upper_point : upperPart)
|
||||
{
|
||||
|
||||
ClosestPolygonPoint lowerPolyPoint = findClosest(upper_point, supporting);
|
||||
Point& lower = lowerPolyPoint.location;
|
||||
|
||||
Point3 lower3 = Point3(lower.X, lower.Y, z0);
|
||||
Point3 upper3 = Point3(upper_point.X, upper_point.Y, z1);
|
||||
|
||||
|
||||
if (firstIter)
|
||||
connection.from = lower3;
|
||||
else
|
||||
connection.segments.emplace_back<>(lower3, WeaveSegmentType::DOWN);
|
||||
|
||||
connection.segments.emplace_back<>(upper3, WeaveSegmentType::UP);
|
||||
last_upper = upper3;
|
||||
|
||||
firstIter = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
} // namespace cura
|
||||
|
||||
+153
@@ -0,0 +1,153 @@
|
||||
#ifndef WEAVER_H
|
||||
#define WEAVER_H
|
||||
|
||||
#include "weaveDataStorage.h"
|
||||
#include "commandSocket.h"
|
||||
#include "settings.h"
|
||||
|
||||
#include "modelFile/modelFile.h" // PrintObject
|
||||
#include "slicer.h"
|
||||
|
||||
#include "utils/polygon.h"
|
||||
#include "utils/polygonUtils.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
/*!
|
||||
* The main weaver / WirePrint / wireframe printing class, which computes the basic paths to be followed.
|
||||
*/
|
||||
class Weaver : public SettingsBase
|
||||
{
|
||||
friend class Wireframe2gcode;
|
||||
private:
|
||||
static const int HIGHER_BEND_NO_STRAIGHTEN = 0;
|
||||
static const int MOVE_TO_STRAIGHTEN = 1;
|
||||
static const int RETRACT_TO_STRAIGHTEN = 2;
|
||||
|
||||
int initial_layer_thickness;
|
||||
int connectionHeight;
|
||||
int extrusionWidth;
|
||||
|
||||
int roof_inset;
|
||||
|
||||
int nozzle_outer_diameter;
|
||||
double nozzle_expansion_angle;
|
||||
int nozzle_clearance;
|
||||
int nozzle_top_diameter;
|
||||
|
||||
|
||||
public:
|
||||
|
||||
Weaver(SettingsBase* settings_base) : SettingsBase(settings_base)
|
||||
{
|
||||
|
||||
initial_layer_thickness = getSettingInMicrons("layer_height_0");
|
||||
connectionHeight = getSettingInMicrons("wireframe_height");
|
||||
|
||||
extrusionWidth = getSettingInMicrons("wall_line_width_x");
|
||||
|
||||
roof_inset = getSettingInMicrons("wireframe_roof_inset");
|
||||
nozzle_outer_diameter = getSettingInMicrons("machine_nozzle_tip_outer_diameter"); // ___ ___ .
|
||||
nozzle_expansion_angle = getSettingInAngleRadians("machine_nozzle_expansion_angle"); // \_U_/ .
|
||||
nozzle_clearance = getSettingInMicrons("wireframe_nozzle_clearance"); // at least line width
|
||||
nozzle_top_diameter = tan(nozzle_expansion_angle) * connectionHeight + nozzle_outer_diameter + nozzle_clearance;
|
||||
}
|
||||
|
||||
/*!
|
||||
* This is the main function for Neith / Weaving / WirePrinting / Webbed printing.
|
||||
* Creates a wireframe for the model consisting of horizontal 'flat' parts and connections between consecutive flat parts consisting of UP moves and diagonally DOWN moves.
|
||||
*
|
||||
* \param object The object for which to create a wireframe print
|
||||
* \param commandSocket the commandSocket
|
||||
*/
|
||||
void weave(PrintObject* object, CommandSocket* commandSocket);
|
||||
|
||||
|
||||
private:
|
||||
WireFrame wireFrame;
|
||||
|
||||
|
||||
/*!
|
||||
* Connect two polygons, chainify the second and generate connections from it, supporting on the first polygon.
|
||||
*
|
||||
* \param supporting The polygons from which to start the connection
|
||||
* \param z0 The height of the \p supporting
|
||||
* \param supported The polygons to be supported by the connection from \p supporting to \p supported
|
||||
* \param z1 the height of \p supported
|
||||
* \param include_last Whether the last full link should be included in the chainified \p parts1 if the last link would be shorter than the normal link size.
|
||||
*/
|
||||
void connect(Polygons& parts0, int z0, Polygons& parts1, int z1, WeaveConnection& result, bool include_last);
|
||||
|
||||
/*!
|
||||
* Convert polygons, such that they consist of segments/links of uniform size, namely \p nozzle_top_diameter.
|
||||
*
|
||||
* \param parts1 The polygons to be chainified
|
||||
* \param start_close_to The point from which to start the first link
|
||||
* \param include_last governs whether the last segment is smaller or grater than the \p nozzle_top_diameter.
|
||||
* If true, the last segment may be smaller.
|
||||
*/
|
||||
void chainify_polygons(Polygons& parts1, Point start_close_to, Polygons& result, bool include_last);
|
||||
|
||||
/*!
|
||||
* The main weaving function.
|
||||
* Generate connections between two polygons.
|
||||
* The connections consist of zig zags of which the zig is a line from a point in \p supported to the closest point in \p supporting
|
||||
* and the zag is a diagonal line from the same point in \p supported to a point in \p supporting
|
||||
* with a distance equal to Weaver::nozzle_top_diameter from the other point in \p supporting of the zig.
|
||||
*
|
||||
* \param supporting The polygons from which to start the connection
|
||||
* \param z0 The height of the \p supporting
|
||||
* \param supported The polygons to be supported by the connection from \p supporting to \p supported
|
||||
* \param z1 the height of \p supported
|
||||
* \param result The resulting connection
|
||||
*/
|
||||
void connect_polygons(Polygons& supporting, int z0, Polygons& supported, int z1, WeaveConnection& result);
|
||||
|
||||
/*!
|
||||
* Creates the roofs and floors which are laid down horizontally.
|
||||
*/
|
||||
void createHorizontalFill(Polygons& lower_top_parts, WeaveLayer& layer, Polygons& layer_above, int z1);
|
||||
|
||||
/*!
|
||||
* Fill roofs starting from the outlines of \p supporting.
|
||||
* The area to be filled in is difference( \p to_be_supported , \p supporting ).
|
||||
*
|
||||
* The basic algorithm performs insets on \p supported until the whole area of \p to_be_supported is filled.
|
||||
* In order to not fill holes in the roof, the hole-areas are unioned with the insets, which results in connections where the UP move has close to zero length;
|
||||
* pieces of the area between two consecutive insets have close to zero distance at these points.
|
||||
* These parts of the horizontal infills are converted into moves by the function \p connections2moves.
|
||||
*
|
||||
* Note that the new inset is computed from the last inset, while the connections are between the last chainified inset and the new chainified inset.
|
||||
*
|
||||
*/
|
||||
void fillRoofs(Polygons& supporting, Polygons& to_be_supported, int direction, int z, WeaveRoof& roofs);
|
||||
|
||||
/*!
|
||||
* Fill floors starting from the outlines of \p supporting.
|
||||
* The area to be filled in is \p floors = difference( \p to_be_supported , \p supporting ).
|
||||
*
|
||||
* The basic algorithm performs outsets until the whole area of [to_be_supported] is filled.
|
||||
* In order to not fill too much, the outsets are intersected with the [floors] area, which results in connections where the UP move has close to zero length.
|
||||
* These parts of the horizontal infills are converted into moves by the function [connections2moves].
|
||||
*
|
||||
* The first supporting polygons are \p supporting while the supporting polygons in consecutive iterations are sub-areas of \p floors.
|
||||
*
|
||||
* Note that the new outset is computed from the last outset, while the connections are between the last chainified outset and the new (chainified) outset.
|
||||
*
|
||||
*/
|
||||
void fillFloors(Polygons& supporting, Polygons& to_be_supported, int direction, int z, WeaveRoof& roofs);
|
||||
|
||||
/*!
|
||||
* Filter out parts of connections with small distances; replace by moves.
|
||||
*
|
||||
*/
|
||||
void connections2moves(WeaveRoofPart& inset);
|
||||
|
||||
};
|
||||
|
||||
}//namespace cura
|
||||
|
||||
#endif//WEAVER_H
|
||||
@@ -0,0 +1,555 @@
|
||||
#include "Wireframe2gcode.h"
|
||||
|
||||
#include <cmath> // sqrt
|
||||
#include <fstream> // debug IO
|
||||
|
||||
#include "weaveDataStorage.h"
|
||||
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
|
||||
void Wireframe2gcode::writeGCode(CommandSocket* commandSocket, int& maxObjectHeight)
|
||||
{
|
||||
|
||||
if (commandSocket)
|
||||
commandSocket->beginGCode();
|
||||
|
||||
maxObjectHeight = wireFrame.layers.back().z1;
|
||||
|
||||
{ // starting Gcode
|
||||
if (hasSetting("material_bed_temperature") && getSettingInDegreeCelsius("material_bed_temperature") > 0)
|
||||
gcode.writeBedTemperatureCommand(getSettingInDegreeCelsius("material_bed_temperature"), true);
|
||||
if (hasSetting("material_print_temperature") && getSettingInDegreeCelsius("material_print_temperature") > 0)
|
||||
gcode.writeTemperatureCommand(getSettingAsIndex("extruder_nr"), getSettingInDegreeCelsius("material_print_temperature"));
|
||||
|
||||
gcode.writeCode(getSettingString("machine_start_gcode").c_str());
|
||||
if (gcode.getFlavor() == GCODE_FLAVOR_BFB)
|
||||
{
|
||||
gcode.writeComment("enable auto-retraction");
|
||||
std::ostringstream tmp;
|
||||
tmp << "M227 S" << (getSettingInMicrons("retraction_amount") * 2560 / 1000) << " P" << (getSettingInMicrons("retraction_amount") * 2560 / 1000); // TODO: put hard coded value in a variable with an explanatory name (and make var a parameter, and perhaps even a setting?)
|
||||
gcode.writeLine(tmp.str().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
unsigned int totalLayers = wireFrame.layers.size();
|
||||
gcode.writeLayerComment(0);
|
||||
gcode.writeTypeComment("SKIRT");
|
||||
|
||||
gcode.setZ(initial_layer_thickness);
|
||||
|
||||
for (PolygonRef bottom_part : wireFrame.bottom_infill.roof_outlines)
|
||||
{
|
||||
if (bottom_part.size() == 0) continue;
|
||||
writeMoveWithRetract(bottom_part[bottom_part.size()-1]);
|
||||
for (Point& segment_to : bottom_part)
|
||||
{
|
||||
gcode.writeMove(segment_to, speedBottom, extrusion_per_mm_flat);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// bottom:
|
||||
Polygons empty_outlines;
|
||||
writeFill(wireFrame.bottom_infill.roof_insets, empty_outlines,
|
||||
[this](Wireframe2gcode& thiss, WeaveRoofPart& inset, WeaveConnectionPart& part, unsigned int segment_idx) {
|
||||
WeaveConnectionSegment& segment = part.connection.segments[segment_idx];
|
||||
if (segment.segmentType == WeaveSegmentType::MOVE || segment.segmentType == WeaveSegmentType::DOWN_AND_FLAT) // this is the case when an inset overlaps with a hole
|
||||
{
|
||||
writeMoveWithRetract(segment.to);
|
||||
} else
|
||||
{
|
||||
gcode.writeMove(segment.to, speedBottom, extrusion_per_mm_connection);
|
||||
}
|
||||
}
|
||||
,
|
||||
[this](Wireframe2gcode& thiss, WeaveConnectionSegment& segment) {
|
||||
if (segment.segmentType == WeaveSegmentType::MOVE)
|
||||
writeMoveWithRetract(segment.to);
|
||||
else if (segment.segmentType == WeaveSegmentType::DOWN_AND_FLAT)
|
||||
return; // do nothing
|
||||
else
|
||||
gcode.writeMove(segment.to, speedBottom, extrusion_per_mm_flat);
|
||||
}
|
||||
);
|
||||
|
||||
for (unsigned int layer_nr = 0; layer_nr < wireFrame.layers.size(); layer_nr++)
|
||||
{
|
||||
|
||||
logProgress("export", layer_nr+1, totalLayers); // abuse the progress system of the normal mode of CuraEngine
|
||||
if (commandSocket) commandSocket->sendProgress(2.0/3.0 + 1.0/3.0 * float(layer_nr) / float(totalLayers));
|
||||
|
||||
WeaveLayer& layer = wireFrame.layers[layer_nr];
|
||||
|
||||
gcode.writeLayerComment(layer_nr+1);
|
||||
|
||||
int fanSpeed = getSettingInPercentage("cool_fan_speed_max");
|
||||
if (layer_nr == 0)
|
||||
fanSpeed = getSettingInPercentage("cool_fan_speed_min");
|
||||
gcode.writeFanCommand(fanSpeed);
|
||||
|
||||
for (unsigned int part_nr = 0; part_nr < layer.connections.size(); part_nr++)
|
||||
{
|
||||
WeaveConnectionPart& part = layer.connections[part_nr];
|
||||
|
||||
if (part.connection.segments.size() == 0) continue;
|
||||
|
||||
gcode.writeTypeComment("SUPPORT"); // connection
|
||||
{
|
||||
if (vSize2(gcode.getPositionXY() - part.connection.from) > connectionHeight)
|
||||
{
|
||||
Point3 point_same_height(part.connection.from.x, part.connection.from.y, layer.z1+100);
|
||||
writeMoveWithRetract(point_same_height);
|
||||
}
|
||||
writeMoveWithRetract(part.connection.from);
|
||||
for (unsigned int segment_idx = 0; segment_idx < part.connection.segments.size(); segment_idx++)
|
||||
{
|
||||
handle_segment(layer, part, segment_idx);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
gcode.writeTypeComment("WALL-OUTER"); // top
|
||||
{
|
||||
for (unsigned int segment_idx = 0; segment_idx < part.connection.segments.size(); segment_idx++)
|
||||
{
|
||||
WeaveConnectionSegment& segment = part.connection.segments[segment_idx];
|
||||
if (segment.segmentType == WeaveSegmentType::DOWN) continue;
|
||||
if (segment.segmentType == WeaveSegmentType::MOVE)
|
||||
{
|
||||
writeMoveWithRetract(segment.to);
|
||||
} else
|
||||
{
|
||||
gcode.writeMove(segment.to, speedFlat, extrusion_per_mm_flat);
|
||||
gcode.writeDelay(flat_delay);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// roofs:
|
||||
gcode.setZ(layer.z1);
|
||||
std::function<void (Wireframe2gcode& thiss, WeaveRoofPart& inset, WeaveConnectionPart& part, unsigned int segment_idx)>
|
||||
handle_roof = &Wireframe2gcode::handle_roof_segment;
|
||||
writeFill(layer.roofs.roof_insets, layer.roofs.roof_outlines,
|
||||
handle_roof,
|
||||
[this](Wireframe2gcode& thiss, WeaveConnectionSegment& segment) { // handle flat segments
|
||||
if (segment.segmentType == WeaveSegmentType::MOVE)
|
||||
{
|
||||
writeMoveWithRetract(segment.to);
|
||||
} else if (segment.segmentType == WeaveSegmentType::DOWN_AND_FLAT)
|
||||
{
|
||||
// do nothing
|
||||
} else
|
||||
{
|
||||
gcode.writeMove(segment.to, speedFlat, extrusion_per_mm_flat);
|
||||
gcode.writeDelay(flat_delay);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
gcode.setZ(maxObjectHeight);
|
||||
|
||||
gcode.writeRetraction(&standard_retraction_config);
|
||||
|
||||
|
||||
gcode.updateTotalPrintTime();
|
||||
|
||||
gcode.writeDelay(0.3);
|
||||
|
||||
gcode.writeFanCommand(0);
|
||||
|
||||
if (commandSocket)
|
||||
{
|
||||
gcode.finalize(maxObjectHeight, getSettingInMillimetersPerSecond("speed_travel"), getSettingString("machine_end_gcode").c_str());
|
||||
for(int e=0; e<MAX_EXTRUDERS; e++)
|
||||
gcode.writeTemperatureCommand(e, 0, false);
|
||||
|
||||
commandSocket->sendGCodeLayer();
|
||||
commandSocket->endSendSlicedObject();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Wireframe2gcode::go_down(WeaveLayer& layer, WeaveConnectionPart& part, unsigned int segment_idx)
|
||||
{
|
||||
WeaveConnectionSegment& segment = part.connection.segments[segment_idx];
|
||||
Point3 from = (segment_idx == 0)? part.connection.from : part.connection.segments[segment_idx - 1].to;
|
||||
if (go_back_to_last_top)
|
||||
gcode.writeMove(from, speedDown, 0);
|
||||
if (straight_first_when_going_down <= 0)
|
||||
{
|
||||
gcode.writeMove(segment.to, speedDown, extrusion_per_mm_connection);
|
||||
} else
|
||||
{
|
||||
Point3& to = segment.to;
|
||||
Point3 from = gcode.getPosition();// segment.from;
|
||||
Point3 vec = to - from;
|
||||
Point3 in_between = from + vec * straight_first_when_going_down / 100;
|
||||
|
||||
Point3 up(in_between.x, in_between.y, from.z);
|
||||
int64_t new_length = (up - from).vSize() + (to - up).vSize() + 5;
|
||||
int64_t orr_length = vec.vSize();
|
||||
double enlargement = new_length / orr_length;
|
||||
gcode.writeMove(up, speedDown*enlargement, extrusion_per_mm_connection / enlargement);
|
||||
gcode.writeMove(to, speedDown*enlargement, extrusion_per_mm_connection / enlargement);
|
||||
}
|
||||
gcode.writeDelay(bottom_delay);
|
||||
if (up_dist_half_speed > 0)
|
||||
{
|
||||
|
||||
gcode.writeMove(Point3(0,0,up_dist_half_speed) + gcode.getPosition(), speedUp / 2, extrusion_per_mm_connection * 2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Wireframe2gcode::strategy_knot(WeaveLayer& layer, WeaveConnectionPart& part, unsigned int segment_idx)
|
||||
{
|
||||
WeaveConnectionSegment& segment = part.connection.segments[segment_idx];
|
||||
gcode.writeMove(segment.to, speedUp, extrusion_per_mm_connection);
|
||||
Point3 next_vector;
|
||||
if (segment_idx + 1 < part.connection.segments.size())
|
||||
{
|
||||
WeaveConnectionSegment& next_segment = part.connection.segments[segment_idx+1];
|
||||
next_vector = next_segment.to - segment.to;
|
||||
} else
|
||||
{
|
||||
next_vector = part.connection.segments[0].to - segment.to;
|
||||
}
|
||||
Point next_dir_2D(next_vector.x, next_vector.y);
|
||||
next_dir_2D = next_dir_2D * top_jump_dist / vSize(next_dir_2D);
|
||||
Point3 next_dir (next_dir_2D.X / 2, next_dir_2D.Y / 2, -top_jump_dist);
|
||||
|
||||
Point3 current_pos = gcode.getPosition();
|
||||
|
||||
gcode.writeMove(current_pos - next_dir, speedUp, 0);
|
||||
gcode.writeDelay(top_delay);
|
||||
gcode.writeMove(current_pos + next_dir_2D, speedUp, 0);
|
||||
}
|
||||
|
||||
void Wireframe2gcode::strategy_retract(WeaveLayer& layer, WeaveConnectionPart& part, unsigned int segment_idx)
|
||||
{
|
||||
WeaveConnectionSegment& segment = part.connection.segments[segment_idx];
|
||||
Point3 from = (segment_idx == 0)? part.connection.from : part.connection.segments[segment_idx - 1].to;
|
||||
|
||||
RetractionConfig retraction_config;
|
||||
// TODO: get these from the settings!
|
||||
retraction_config.amount = 500; //INT2MM(getSettingInt("retractionAmount"))
|
||||
retraction_config.primeAmount = 0;//INT2MM(getSettingInt("retractionPrime
|
||||
retraction_config.speed = 20; // 40;
|
||||
retraction_config.primeSpeed = 15; // 30;
|
||||
retraction_config.zHop = 0; //getSettingInt("retraction_hop");
|
||||
|
||||
double top_retract_pause = 2.0;
|
||||
int retract_hop_dist = 1000;
|
||||
bool after_retract_hop = false;
|
||||
//bool go_horizontal_first = true;
|
||||
bool lower_retract_start = true;
|
||||
|
||||
|
||||
Point3& to = segment.to;
|
||||
if (lower_retract_start)
|
||||
{
|
||||
Point3 vec = to - from;
|
||||
Point3 lowering = vec * retract_hop_dist / 2 / vec.vSize();
|
||||
Point3 lower = to - lowering;
|
||||
gcode.writeMove(lower, speedUp, extrusion_per_mm_connection);
|
||||
gcode.writeRetraction(&retraction_config);
|
||||
gcode.writeMove(to + lowering, speedUp, 0);
|
||||
gcode.writeDelay(top_retract_pause);
|
||||
if (after_retract_hop)
|
||||
gcode.writeMove(to + Point3(0, 0, retract_hop_dist), speedFlat, 0);
|
||||
|
||||
} else
|
||||
{
|
||||
gcode.writeMove(to, speedUp, extrusion_per_mm_connection);
|
||||
gcode.writeRetraction(&retraction_config);
|
||||
gcode.writeMove(to + Point3(0, 0, retract_hop_dist), speedFlat, 0);
|
||||
gcode.writeDelay(top_retract_pause);
|
||||
if (after_retract_hop)
|
||||
gcode.writeMove(to + Point3(0, 0, retract_hop_dist*3), speedFlat, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void Wireframe2gcode::strategy_compensate(WeaveLayer& layer, WeaveConnectionPart& part, unsigned int segment_idx)
|
||||
{
|
||||
WeaveConnectionSegment& segment = part.connection.segments[segment_idx];
|
||||
Point3 from = (segment_idx == 0)? part.connection.from : part.connection.segments[segment_idx - 1].to;
|
||||
Point3 to = segment.to + Point3(0, 0, fall_down*(segment.to - from).vSize() / connectionHeight);
|
||||
Point3 vector = segment.to - from;
|
||||
Point3 dir = vector * drag_along / vector.vSize();
|
||||
|
||||
Point3 next_point;
|
||||
if (segment_idx + 1 < part.connection.segments.size())
|
||||
{
|
||||
WeaveConnectionSegment& next_segment = part.connection.segments[segment_idx+1];
|
||||
next_point = next_segment.to;
|
||||
} else
|
||||
{
|
||||
next_point = part.connection.segments[0].to;
|
||||
}
|
||||
Point3 next_vector = next_point - segment.to;
|
||||
Point next_dir_2D(next_vector.x, next_vector.y);
|
||||
int64_t next_dir_2D_size = vSize(next_dir_2D);
|
||||
if (next_dir_2D_size > 0)
|
||||
next_dir_2D = next_dir_2D * drag_along / next_dir_2D_size;
|
||||
Point3 next_dir (next_dir_2D.X, next_dir_2D.Y, 0);
|
||||
|
||||
Point3 newTop = to - next_dir + dir;
|
||||
|
||||
int64_t orrLength = (segment.to - from).vSize() + next_vector.vSize() + 1; // + 1 in order to avoid division by zero
|
||||
int64_t newLength = (newTop - from).vSize() + (next_point - newTop).vSize() + 1; // + 1 in order to avoid division by zero
|
||||
|
||||
gcode.writeMove(newTop, speedUp * newLength / orrLength, extrusion_per_mm_connection * orrLength / newLength);
|
||||
}
|
||||
void Wireframe2gcode::handle_segment(WeaveLayer& layer, WeaveConnectionPart& part, unsigned int segment_idx)
|
||||
{
|
||||
WeaveConnectionSegment& segment = part.connection.segments[segment_idx];
|
||||
|
||||
switch(segment.segmentType)
|
||||
{
|
||||
case WeaveSegmentType::MOVE:
|
||||
writeMoveWithRetract(segment.to);
|
||||
break;
|
||||
case WeaveSegmentType::DOWN:
|
||||
go_down(layer, part, segment_idx);
|
||||
break;
|
||||
case WeaveSegmentType::FLAT:
|
||||
DEBUG_SHOW("flat piece in connection?!!?!");
|
||||
break;
|
||||
case WeaveSegmentType::UP:
|
||||
if (strategy == STRATEGY_KNOT)
|
||||
{
|
||||
strategy_knot(layer, part, segment_idx);
|
||||
} else if (strategy == STRATEGY_RETRACT)
|
||||
{
|
||||
strategy_retract(layer, part, segment_idx);
|
||||
} else if (strategy == STRATEGY_COMPENSATE)
|
||||
{
|
||||
strategy_compensate(layer, part, segment_idx);
|
||||
}
|
||||
break;
|
||||
case WeaveSegmentType::DOWN_AND_FLAT:
|
||||
logError("Down and flat move in non-horizontal connection!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void Wireframe2gcode::handle_roof_segment(WeaveRoofPart& inset, WeaveConnectionPart& part, unsigned int segment_idx)
|
||||
{
|
||||
WeaveConnectionSegment& segment = part.connection.segments[segment_idx];
|
||||
Point3 from = (segment_idx == 0)? part.connection.from : part.connection.segments[segment_idx - 1].to;
|
||||
WeaveConnectionSegment* next_segment = nullptr;
|
||||
if (segment_idx + 1 < part.connection.segments.size())
|
||||
next_segment = &part.connection.segments[segment_idx+1];
|
||||
switch(segment.segmentType)
|
||||
{
|
||||
case WeaveSegmentType::MOVE:
|
||||
case WeaveSegmentType::DOWN_AND_FLAT:
|
||||
if (next_segment && next_segment->segmentType != WeaveSegmentType::DOWN_AND_FLAT)
|
||||
{
|
||||
writeMoveWithRetract(segment.to);
|
||||
}
|
||||
break;
|
||||
case WeaveSegmentType::UP:
|
||||
{
|
||||
Point3 to = segment.to + Point3(0, 0, roof_fall_down);
|
||||
|
||||
Point3 vector = segment.to - from;
|
||||
if (vector.vSize2() == 0) return;
|
||||
Point3 dir = vector * roof_drag_along / vector.vSize();
|
||||
|
||||
Point3 next_vector;
|
||||
if (next_segment)
|
||||
{
|
||||
next_vector = next_segment->to - segment.to;
|
||||
} else
|
||||
{
|
||||
next_vector = part.connection.segments[0].to - segment.to;
|
||||
}
|
||||
Point next_dir_2D(next_vector.x, next_vector.y);
|
||||
Point3 detoured = to + dir;
|
||||
if (vSize2(next_dir_2D) > 0)
|
||||
{
|
||||
next_dir_2D = next_dir_2D * roof_drag_along / vSize(next_dir_2D);
|
||||
Point3 next_dir (next_dir_2D.X, next_dir_2D.Y, 0);
|
||||
detoured -= next_dir;
|
||||
}
|
||||
|
||||
gcode.writeMove(detoured, speedUp, extrusion_per_mm_connection);
|
||||
|
||||
}
|
||||
break;
|
||||
case WeaveSegmentType::DOWN:
|
||||
gcode.writeMove(segment.to, speedDown, extrusion_per_mm_connection);
|
||||
gcode.writeDelay(roof_outer_delay);
|
||||
break;
|
||||
case WeaveSegmentType::FLAT:
|
||||
logError("Flat move in connection!");
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Wireframe2gcode::writeFill(std::vector<WeaveRoofPart>& fill_insets, Polygons& roof_outlines
|
||||
, std::function<void (Wireframe2gcode& thiss, WeaveRoofPart& inset, WeaveConnectionPart& part, unsigned int segment_idx)> connectionHandler
|
||||
, std::function<void (Wireframe2gcode& thiss, WeaveConnectionSegment& p)> flatHandler)
|
||||
{
|
||||
|
||||
// bottom:
|
||||
gcode.writeTypeComment("FILL");
|
||||
for (unsigned int inset_idx = 0; inset_idx < fill_insets.size(); inset_idx++)
|
||||
{
|
||||
WeaveRoofPart& inset = fill_insets[inset_idx];
|
||||
|
||||
|
||||
for (unsigned int inset_part_nr = 0; inset_part_nr < inset.connections.size(); inset_part_nr++)
|
||||
{
|
||||
WeaveConnectionPart& inset_part = inset.connections[inset_part_nr];
|
||||
std::vector<WeaveConnectionSegment>& segments = inset_part.connection.segments;
|
||||
|
||||
gcode.writeTypeComment("SUPPORT"); // connection
|
||||
if (segments.size() == 0) continue;
|
||||
Point3 first_extrusion_from = inset_part.connection.from;
|
||||
unsigned int first_segment_idx;
|
||||
for (first_segment_idx = 0; first_segment_idx < segments.size() && segments[first_segment_idx].segmentType == WeaveSegmentType::MOVE; first_segment_idx++)
|
||||
{ // finds the first segment which is not a move
|
||||
first_extrusion_from = segments[first_segment_idx].to;
|
||||
}
|
||||
if (first_segment_idx == segments.size())
|
||||
continue;
|
||||
writeMoveWithRetract(first_extrusion_from);
|
||||
for (unsigned int segment_idx = first_segment_idx; segment_idx < segments.size(); segment_idx++)
|
||||
{
|
||||
connectionHandler(*this, inset, inset_part, segment_idx);
|
||||
}
|
||||
|
||||
gcode.writeTypeComment("WALL-INNER"); // top
|
||||
for (unsigned int segment_idx = 0; segment_idx < segments.size(); segment_idx++)
|
||||
{
|
||||
WeaveConnectionSegment& segment = segments[segment_idx];
|
||||
|
||||
if (segment.segmentType == WeaveSegmentType::DOWN) continue;
|
||||
|
||||
flatHandler(*this, segment);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
gcode.writeTypeComment("WALL-OUTER"); // outer perimeter of the flat parts
|
||||
for (PolygonRef poly : roof_outlines)
|
||||
{
|
||||
writeMoveWithRetract(poly[poly.size() - 1]);
|
||||
for (Point& p : poly)
|
||||
{
|
||||
Point3 to(p.X, p.Y, gcode.getPositionZ());
|
||||
WeaveConnectionSegment segment(to, WeaveSegmentType::FLAT);
|
||||
flatHandler(*this, segment);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void Wireframe2gcode::writeMoveWithRetract(Point3 to)
|
||||
{
|
||||
if ((gcode.getPosition() - to).vSize2() >= nozzle_top_diameter * nozzle_top_diameter * 2 * 2)
|
||||
gcode.writeRetraction(&standard_retraction_config);
|
||||
gcode.writeMove(to, moveSpeed, 0);
|
||||
}
|
||||
|
||||
void Wireframe2gcode::writeMoveWithRetract(Point to)
|
||||
{
|
||||
if (vSize2(gcode.getPositionXY() - to) >= nozzle_top_diameter * nozzle_top_diameter * 2 * 2)
|
||||
gcode.writeRetraction(&standard_retraction_config);
|
||||
gcode.writeMove(to, moveSpeed, 0);
|
||||
}
|
||||
|
||||
Wireframe2gcode::Wireframe2gcode(Weaver& weaver, GCodeExport& gcode, SettingsBase* settings_base)
|
||||
: SettingsBase(settings_base)
|
||||
, gcode(gcode)
|
||||
{
|
||||
wireFrame = weaver.wireFrame;
|
||||
initial_layer_thickness = getSettingInMicrons("layer_height_0");
|
||||
connectionHeight = getSettingInMicrons("wireframe_height");
|
||||
roof_inset = getSettingInMicrons("wireframe_roof_inset");
|
||||
|
||||
filament_diameter = getSettingInMicrons("material_diameter");
|
||||
extrusionWidth = getSettingInMicrons("wall_line_width_x");
|
||||
|
||||
flowConnection = getSettingInPercentage("wireframe_flow_connection");
|
||||
flowFlat = getSettingInPercentage("wireframe_flow_flat");
|
||||
|
||||
double filament_area = /* M_PI * */ (INT2MM(filament_diameter) / 2.0) * (INT2MM(filament_diameter) / 2.0);
|
||||
double lineArea = /* M_PI * */ (INT2MM(extrusionWidth) / 2.0) * (INT2MM(extrusionWidth) / 2.0);
|
||||
extrusion_per_mm_connection = lineArea / filament_area * double(flowConnection) / 100.0;
|
||||
extrusion_per_mm_flat = lineArea / filament_area * double(flowFlat) / 100.0;
|
||||
|
||||
nozzle_outer_diameter = getSettingInMicrons("machine_nozzle_tip_outer_diameter"); // ___ ___ .
|
||||
nozzle_head_distance = getSettingInMicrons("machine_nozzle_head_distance"); // | | .
|
||||
nozzle_expansion_angle = getSettingInAngleRadians("machine_nozzle_expansion_angle"); // \_U_/ .
|
||||
nozzle_clearance = getSettingInMicrons("wireframe_nozzle_clearance"); // at least line width
|
||||
nozzle_top_diameter = tan(nozzle_expansion_angle) * connectionHeight + nozzle_outer_diameter + nozzle_clearance;
|
||||
|
||||
moveSpeed = 40;
|
||||
speedBottom = getSettingInMillimetersPerSecond("wireframe_printspeed_bottom");
|
||||
speedUp = getSettingInMillimetersPerSecond("wireframe_printspeed_up");
|
||||
speedDown = getSettingInMillimetersPerSecond("wireframe_printspeed_down");
|
||||
speedFlat = getSettingInMillimetersPerSecond("wireframe_printspeed_flat");
|
||||
|
||||
flat_delay = getSettingInSeconds("wireframe_flat_delay");
|
||||
bottom_delay = getSettingInSeconds("wireframe_bottom_delay");
|
||||
top_delay = getSettingInSeconds("wireframe_top_delay");
|
||||
|
||||
up_dist_half_speed = getSettingInMicrons("wireframe_up_half_speed");
|
||||
|
||||
top_jump_dist = getSettingInMicrons("wireframe_top_jump");
|
||||
|
||||
fall_down = getSettingInMicrons("wireframe_fall_down");
|
||||
drag_along = getSettingInMicrons("wireframe_drag_along");
|
||||
|
||||
strategy = STRATEGY_COMPENSATE;
|
||||
if (getSettingString("wireframe_strategy") == "Compensate")
|
||||
strategy = STRATEGY_COMPENSATE;
|
||||
if (getSettingString("wireframe_strategy") == "Knot")
|
||||
strategy = STRATEGY_KNOT;
|
||||
if (getSettingString("wireframe_strategy") == "Retract")
|
||||
strategy = STRATEGY_RETRACT;
|
||||
|
||||
go_back_to_last_top = false;
|
||||
straight_first_when_going_down = getSettingInPercentage("wireframe_straight_before_down");
|
||||
|
||||
roof_fall_down = getSettingInMicrons("wireframe_roof_fall_down");
|
||||
roof_drag_along = getSettingInMicrons("wireframe_roof_drag_along");
|
||||
roof_outer_delay = getSettingInSeconds("wireframe_roof_outer_delay");
|
||||
|
||||
|
||||
standard_retraction_config.amount = INT2MM(getSettingInMicrons("retraction_amount"));
|
||||
standard_retraction_config.primeAmount = INT2MM(getSettingInMicrons("retraction_extra_prime_amount"));
|
||||
standard_retraction_config.speed = getSettingInMillimetersPerSecond("retraction_retract_speed");
|
||||
standard_retraction_config.primeSpeed = getSettingInMillimetersPerSecond("retraction_prime_speed");
|
||||
standard_retraction_config.zHop = getSettingInMicrons("retraction_hop");
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
} // namespace cura
|
||||
@@ -0,0 +1,155 @@
|
||||
#ifndef WIREFRAME2GCODE_H
|
||||
#define WIREFRAME2GCODE_H
|
||||
|
||||
|
||||
#include <functional> // passing function pointer or lambda as argument to a function
|
||||
|
||||
#include "weaveDataStorage.h"
|
||||
#include "commandSocket.h"
|
||||
#include "settings.h"
|
||||
|
||||
#include "modelFile/modelFile.h" // PrintObject
|
||||
#include "slicer.h"
|
||||
|
||||
#include "utils/polygon.h"
|
||||
#include "Weaver.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
/*!
|
||||
* Export class for exporting wireframe print gcode / weaver gcode / wireprint gcode.
|
||||
*/
|
||||
class Wireframe2gcode : public SettingsBase
|
||||
{
|
||||
private:
|
||||
static const int STRATEGY_COMPENSATE = 0;
|
||||
static const int STRATEGY_KNOT = 1;
|
||||
static const int STRATEGY_RETRACT = 2;
|
||||
|
||||
int initial_layer_thickness;
|
||||
int filament_diameter;
|
||||
int extrusionWidth;
|
||||
int flowConnection;// = getSettingInt("wireframeFlowConnection");
|
||||
int flowFlat; // = getSettingInt("wireframeFlowFlat");
|
||||
double extrusion_per_mm_connection; // = lineArea / filament_area * double(flowConnection) / 100.0;
|
||||
double extrusion_per_mm_flat; // = lineArea / filament_area * double(flowFlat) / 100.0;
|
||||
int nozzle_outer_diameter; // = getSettingInt("machineNozzleTipOuterDiameter"); // ___ ___ .
|
||||
int nozzle_head_distance; // = getSettingInt("machineNozzleHeadDistance"); // | | .
|
||||
int nozzle_expansion_angle; // = getSettingInt("machineNozzleExpansionAngle"); // \_U_/ .
|
||||
int nozzle_clearance; // = getSettingInt("wireframeNozzleClearance"); // at least line width
|
||||
int nozzle_top_diameter; // = tan(static_cast<double>(nozzle_expansion_angle)/180.0 * M_PI) * connectionHeight + nozzle_outer_diameter + nozzle_clearance;
|
||||
int moveSpeed; // = 40;
|
||||
int speedBottom; // = getSettingInt("wireframePrintspeedBottom");
|
||||
int speedUp; // = getSettingInt("wireframePrintspeedUp");
|
||||
int speedDown; // = getSettingInt("wireframePrintspeedDown");
|
||||
int speedFlat; // = getSettingInt("wireframePrintspeedFlat");
|
||||
int connectionHeight; // = getSettingInt("wireframeConnectionHeight");
|
||||
int roof_inset; // = getSettingInt("wireframeRoofInset");
|
||||
double flat_delay; // = getSettingInt("wireframeFlatDelay")/100.0;
|
||||
double bottom_delay; // = getSettingInt("wireframeBottomDelay")/100.0;
|
||||
double top_delay; // = getSettingInt("wireframeTopDelay")/100.0;
|
||||
int up_dist_half_speed; // = getSettingInt("wireframeUpDistHalfSpeed");
|
||||
int top_jump_dist; // = getSettingInt("wireframeTopJump");
|
||||
int fall_down; // = getSettingInt("wireframeFallDown");
|
||||
int drag_along; // = getSettingInt("wireframeDragAlong");
|
||||
int strategy; // = getSettingInt("wireframeStrategy"); // HIGHER_BEND_NO_STRAIGHTEN; // RETRACT_TO_STRAIGHTEN; // MOVE_TO_STRAIGHTEN; //
|
||||
double go_back_to_last_top; // = false;
|
||||
int straight_first_when_going_down; // = getSettingInt("wireframeStraightBeforeDown"); // %
|
||||
int roof_fall_down; // = getSettingInt("wireframeRoofFallDown");
|
||||
int roof_drag_along; // = getSettingInt("wireframeRoofDragAlong");
|
||||
double roof_outer_delay; // = getSettingInt("wireframeRoofOuterDelay")/100.0;
|
||||
|
||||
RetractionConfig standard_retraction_config; //!< The standard retraction settings used for moves between parts etc.
|
||||
|
||||
public:
|
||||
GCodeExport& gcode; //!< Where the result is 'stored'
|
||||
|
||||
Wireframe2gcode(Weaver& weaver, GCodeExport& gcode, SettingsBase* settings_base);
|
||||
|
||||
void writeGCode(CommandSocket* commandSocket, int& maxObjectHeight);
|
||||
|
||||
|
||||
private:
|
||||
WireFrame wireFrame;
|
||||
|
||||
void writeFill(std::vector<WeaveRoofPart>& fill_insets, Polygons& outlines
|
||||
, std::function<void (Wireframe2gcode& thiss, WeaveRoofPart& inset, WeaveConnectionPart& part, unsigned int segment_idx)> connectionHandler
|
||||
, std::function<void (Wireframe2gcode& thiss, WeaveConnectionSegment& p)> flatHandler);
|
||||
|
||||
/*!
|
||||
* Function for writing the gcode for a diagonally down movement of a connection.
|
||||
*
|
||||
* \param layer The layer in which the segment is
|
||||
* \param part The part in which the segment is
|
||||
* \param segment_idx The index of the segment in the \p part
|
||||
*/
|
||||
void go_down(WeaveLayer& layer, WeaveConnectionPart& part, unsigned int segment_idx);
|
||||
|
||||
/*!
|
||||
* Function for writing the gcode of an upward move of a connection, which does a couple of small moves at the top.
|
||||
*
|
||||
* \param layer The layer in which the segment is
|
||||
* \param part The part in which the segment is
|
||||
* \param segment_idx The index of the segment in the \p part
|
||||
*/
|
||||
void strategy_knot(WeaveLayer& layer, WeaveConnectionPart& part, unsigned int segment_idx);
|
||||
|
||||
/*!
|
||||
* Function for writing the gcode of an upward move of a connection, which does a retract at the top.
|
||||
*
|
||||
* \param layer The layer in which the segment is
|
||||
* \param part The part in which the segment is
|
||||
* \param segment_idx The index of the segment in the \p part
|
||||
*/
|
||||
void strategy_retract(WeaveLayer& layer, WeaveConnectionPart& part, unsigned int segment_idx);
|
||||
|
||||
/*!
|
||||
* Function for writing the gcode of an upward move of a connection, which goes Wireframe2gcode::fall_down further up
|
||||
* and Wireframe2gcode::drag_along back from the direction it will go to next.
|
||||
*
|
||||
* \param layer The layer in which the segment is
|
||||
* \param part The part in which the segment is
|
||||
* \param segment_idx The index of the segment in the \p part
|
||||
*/
|
||||
void strategy_compensate(WeaveLayer& layer, WeaveConnectionPart& part, unsigned int segment_idx);
|
||||
|
||||
/*!
|
||||
* Function writing the gcode of a segment in the connection between two layers.
|
||||
*
|
||||
* \param layer The layer in which the segment is
|
||||
* \param part The part in which the segment is
|
||||
* \param segment_idx The index of the segment in the \p part
|
||||
*/
|
||||
void handle_segment(WeaveLayer& layer, WeaveConnectionPart& part, unsigned int segment_idx);
|
||||
|
||||
/*!
|
||||
* Function for writing the gcode of a segment in the connection between two roof insets / floor outsets.
|
||||
*
|
||||
* \param inset The inset in which the segment is
|
||||
* \param part the part in which the segment is
|
||||
* \param segment_idx The index of the segment in the \p part
|
||||
*/
|
||||
void handle_roof_segment(WeaveRoofPart& inset, WeaveConnectionPart& part, unsigned int segment_idx);
|
||||
|
||||
/*!
|
||||
* Write a move action to gcode, inserting a retraction if neccesary.
|
||||
*
|
||||
* \param to The 3D destination of the move
|
||||
*/
|
||||
void writeMoveWithRetract(Point3 to);
|
||||
|
||||
/*!
|
||||
* Write a move action to gcode, inserting a retraction if neccesary.
|
||||
*
|
||||
* \param to The 2D destination of the move
|
||||
*/
|
||||
void writeMoveWithRetract(Point to);
|
||||
|
||||
};
|
||||
|
||||
}//namespace cura
|
||||
|
||||
#endif//WIREFRAME2GCODE_H
|
||||
+2
-2
@@ -148,7 +148,7 @@ bool Comb::moveInside(Point* p, int distance)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Comb::calc(Point startPoint, Point endPoint, vector<Point>& combPoints)
|
||||
bool Comb::calc(Point startPoint, Point endPoint, std::vector<Point>& combPoints)
|
||||
{
|
||||
if (shorterThen(endPoint - startPoint, MM2INT(1.5)))
|
||||
return true;
|
||||
@@ -180,7 +180,7 @@ bool Comb::calc(Point startPoint, Point endPoint, vector<Point>& combPoints)
|
||||
calcMinMax();
|
||||
|
||||
int64_t x = sp.X;
|
||||
vector<Point> pointList;
|
||||
std::vector<Point> pointList;
|
||||
//Now walk trough the crossings, for every boundary we cross, find the initial cross point and the exit point. Then add all the points in between
|
||||
// to the pointList and continue with the next boundary we will cross, until there are no more boundaries to cross.
|
||||
// This gives a path from the start to finish curved around the holes that it encounters.
|
||||
|
||||
+1
-1
@@ -36,7 +36,7 @@ public:
|
||||
bool inside(const Point p) { return boundery.inside(p); }
|
||||
bool moveInside(Point* p, int distance = 100);
|
||||
|
||||
bool calc(Point startPoint, Point endPoint, vector<Point>& combPoints);
|
||||
bool calc(Point startPoint, Point endPoint, std::vector<Point>& combPoints);
|
||||
};
|
||||
|
||||
}//namespace cura
|
||||
|
||||
@@ -0,0 +1,274 @@
|
||||
#include "utils/logoutput.h"
|
||||
#include "commandSocket.h"
|
||||
#include "fffProcessor.h"
|
||||
|
||||
#include <thread>
|
||||
#include <cinttypes>
|
||||
|
||||
#include <Arcus/Socket.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
namespace cura {
|
||||
|
||||
#define BYTES_PER_FLOAT 4
|
||||
#define FLOATS_PER_VECTOR 3
|
||||
#define VECTORS_PER_FACE 3
|
||||
|
||||
class CommandSocket::Private
|
||||
{
|
||||
public:
|
||||
Private()
|
||||
: processor(nullptr)
|
||||
, socket(nullptr)
|
||||
, object_count(0)
|
||||
, current_object_number(0)
|
||||
, currentSlicedObject(nullptr)
|
||||
, slicedObjects(0)
|
||||
{ }
|
||||
|
||||
Cura::Layer* getLayerById(int id);
|
||||
|
||||
fffProcessor* processor;
|
||||
|
||||
Arcus::Socket* socket;
|
||||
|
||||
int object_count;
|
||||
int current_object_number;
|
||||
|
||||
std::shared_ptr<Cura::SlicedObjectList> slicedObjectList;
|
||||
Cura::SlicedObject* currentSlicedObject;
|
||||
int slicedObjects;
|
||||
std::vector<int64_t> objectIds;
|
||||
|
||||
std::string tempGCodeFile;
|
||||
std::ostringstream gcode_output_stream;
|
||||
|
||||
std::shared_ptr<PrintObject> objectToSlice;
|
||||
};
|
||||
|
||||
CommandSocket::CommandSocket(fffProcessor* processor)
|
||||
: d(new Private)
|
||||
{
|
||||
d->processor = processor;
|
||||
d->processor->setCommandSocket(this);
|
||||
}
|
||||
|
||||
void CommandSocket::connect(const std::string& ip, int port)
|
||||
{
|
||||
d->socket = new Arcus::Socket();
|
||||
d->socket->registerMessageType(1, &Cura::ObjectList::default_instance());
|
||||
d->socket->registerMessageType(2, &Cura::SlicedObjectList::default_instance());
|
||||
d->socket->registerMessageType(3, &Cura::Progress::default_instance());
|
||||
d->socket->registerMessageType(4, &Cura::GCodeLayer::default_instance());
|
||||
d->socket->registerMessageType(5, &Cura::ObjectPrintTime::default_instance());
|
||||
d->socket->registerMessageType(6, &Cura::SettingList::default_instance());
|
||||
d->socket->registerMessageType(7, &Cura::GCodePrefix::default_instance());
|
||||
|
||||
d->socket->connect(ip, port);
|
||||
|
||||
while(d->socket->state() != Arcus::SocketState::Closed && d->socket->state() != Arcus::SocketState::Error)
|
||||
{
|
||||
if(d->objectToSlice)
|
||||
{
|
||||
//TODO: Support all-at-once/one-at-a-time printing
|
||||
d->processor->processModel(d->objectToSlice.get());
|
||||
d->objectToSlice.reset();
|
||||
d->processor->resetFileNumber();
|
||||
|
||||
sendPrintTime();
|
||||
}
|
||||
|
||||
Arcus::MessagePtr message = d->socket->takeNextMessage();
|
||||
|
||||
Cura::SettingList* settingList = dynamic_cast<Cura::SettingList*>(message.get());
|
||||
if(settingList)
|
||||
{
|
||||
handleSettingList(settingList);
|
||||
}
|
||||
|
||||
Cura::ObjectList* objectList = dynamic_cast<Cura::ObjectList*>(message.get());
|
||||
if(objectList)
|
||||
{
|
||||
handleObjectList(objectList);
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(250));
|
||||
|
||||
if(!d->socket->errorString().empty()) {
|
||||
logError("%s\n", d->socket->errorString().data());
|
||||
d->socket->clearError();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CommandSocket::handleObjectList(Cura::ObjectList* list)
|
||||
{
|
||||
FMatrix3x3 matrix;
|
||||
d->object_count = 0;
|
||||
d->objectIds.clear();
|
||||
|
||||
d->objectToSlice = std::make_shared<PrintObject>(d->processor);
|
||||
for(auto object : list->objects())
|
||||
{
|
||||
d->objectToSlice->meshes.emplace_back(d->objectToSlice.get()); //Construct a new mesh and put it into printObject's mesh list.
|
||||
Mesh& mesh = d->objectToSlice->meshes.back();
|
||||
|
||||
int bytesPerFace = BYTES_PER_FLOAT * FLOATS_PER_VECTOR * VECTORS_PER_FACE;
|
||||
int faceCount = object.vertices().size() / bytesPerFace;
|
||||
for(int i = 0; i < faceCount; ++i)
|
||||
{
|
||||
//TODO: Apply matrix
|
||||
std::string data = object.vertices().substr(i * bytesPerFace, bytesPerFace);
|
||||
const FPoint3* floatVerts = reinterpret_cast<const FPoint3*>(data.data());
|
||||
|
||||
Point3 verts[3];
|
||||
verts[0] = matrix.apply(floatVerts[0]);
|
||||
verts[1] = matrix.apply(floatVerts[1]);
|
||||
verts[2] = matrix.apply(floatVerts[2]);
|
||||
mesh.addFace(verts[0], verts[1], verts[2]);
|
||||
}
|
||||
|
||||
for(auto setting : object.settings())
|
||||
{
|
||||
mesh.setSetting(setting.name(), setting.value());
|
||||
}
|
||||
|
||||
d->objectIds.push_back(object.id());
|
||||
mesh.finish();
|
||||
}
|
||||
d->object_count++;
|
||||
d->objectToSlice->finalize();
|
||||
}
|
||||
|
||||
void CommandSocket::handleSettingList(Cura::SettingList* list)
|
||||
{
|
||||
for(auto setting : list->settings())
|
||||
{
|
||||
d->processor->setSetting(setting.name(), setting.value());
|
||||
}
|
||||
}
|
||||
|
||||
void CommandSocket::sendLayerInfo(int layer_nr, int32_t z, int32_t height)
|
||||
{
|
||||
if(!d->currentSlicedObject)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Cura::Layer* layer = d->getLayerById(layer_nr);
|
||||
layer->set_height(z);
|
||||
layer->set_thickness(height);
|
||||
}
|
||||
|
||||
void CommandSocket::sendPolygons(PolygonType type, int layer_nr, Polygons& polygons, int line_width)
|
||||
{
|
||||
if(!d->currentSlicedObject)
|
||||
return;
|
||||
|
||||
if (polygons.size() == 0)
|
||||
return;
|
||||
|
||||
Cura::Layer* layer = d->getLayerById(layer_nr);
|
||||
|
||||
for(unsigned int i = 0; i < polygons.size(); ++i)
|
||||
{
|
||||
Cura::Polygon* p = layer->add_polygons();
|
||||
p->set_type(static_cast<Cura::Polygon_Type>(type));
|
||||
std::string polydata;
|
||||
polydata.append(reinterpret_cast<const char*>(polygons[i].data()), polygons[i].size() * sizeof(Point));
|
||||
p->set_points(polydata);
|
||||
p->set_line_width(line_width);
|
||||
}
|
||||
}
|
||||
|
||||
void CommandSocket::sendProgress(float amount)
|
||||
{
|
||||
auto message = std::make_shared<Cura::Progress>();
|
||||
message->set_amount(amount);
|
||||
d->socket->sendMessage(message);
|
||||
}
|
||||
|
||||
void CommandSocket::sendPrintTime()
|
||||
{
|
||||
auto message = std::make_shared<Cura::ObjectPrintTime>();
|
||||
message->set_time(d->processor->getTotalPrintTime());
|
||||
message->set_material_amount(d->processor->getTotalFilamentUsed(0));
|
||||
d->socket->sendMessage(message);
|
||||
}
|
||||
|
||||
void CommandSocket::sendPrintMaterialForObject(int index, int extruder_nr, float print_time)
|
||||
{
|
||||
// socket.sendInt32(CMD_OBJECT_PRINT_MATERIAL);
|
||||
// socket.sendInt32(12);
|
||||
// socket.sendInt32(index);
|
||||
// socket.sendInt32(extruder_nr);
|
||||
// socket.sendFloat32(print_time);
|
||||
}
|
||||
|
||||
void CommandSocket::beginSendSlicedObject()
|
||||
{
|
||||
if(!d->slicedObjectList)
|
||||
{
|
||||
d->slicedObjectList = std::make_shared<Cura::SlicedObjectList>();
|
||||
}
|
||||
|
||||
d->currentSlicedObject = d->slicedObjectList->add_objects();
|
||||
d->currentSlicedObject->set_id(d->objectIds[d->slicedObjects]);
|
||||
}
|
||||
|
||||
void CommandSocket::endSendSlicedObject()
|
||||
{
|
||||
d->slicedObjects++;
|
||||
if(d->slicedObjects >= d->object_count)
|
||||
{
|
||||
d->socket->sendMessage(d->slicedObjectList);
|
||||
d->slicedObjects = 0;
|
||||
d->slicedObjectList.reset();
|
||||
d->currentSlicedObject = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void CommandSocket::beginGCode()
|
||||
{
|
||||
d->processor->setTargetStream(&d->gcode_output_stream);
|
||||
}
|
||||
|
||||
void CommandSocket::sendGCodeLayer()
|
||||
{
|
||||
auto message = std::make_shared<Cura::GCodeLayer>();
|
||||
message->set_id(d->objectIds[0]);
|
||||
message->set_data(d->gcode_output_stream.str());
|
||||
d->socket->sendMessage(message);
|
||||
|
||||
d->gcode_output_stream.str("");
|
||||
}
|
||||
|
||||
void CommandSocket::sendGCodePrefix(std::string prefix)
|
||||
{
|
||||
auto message = std::make_shared<Cura::GCodePrefix>();
|
||||
message->set_data(prefix);
|
||||
d->socket->sendMessage(message);
|
||||
}
|
||||
|
||||
Cura::Layer* CommandSocket::Private::getLayerById(int id)
|
||||
{
|
||||
auto itr = std::find_if(currentSlicedObject->mutable_layers()->begin(), currentSlicedObject->mutable_layers()->end(), [id](Cura::Layer& l) { return l.id() == id; });
|
||||
|
||||
Cura::Layer* layer = nullptr;
|
||||
if(itr != currentSlicedObject->mutable_layers()->end())
|
||||
{
|
||||
layer = &(*itr);
|
||||
}
|
||||
else
|
||||
{
|
||||
layer = currentSlicedObject->add_layers();
|
||||
layer->set_id(id);
|
||||
}
|
||||
|
||||
return layer;
|
||||
}
|
||||
|
||||
}//namespace cura
|
||||
@@ -0,0 +1,45 @@
|
||||
#ifndef COMMAND_SOCKET_H
|
||||
#define COMMAND_SOCKET_H
|
||||
|
||||
#include "utils/socket.h"
|
||||
#include "utils/polygon.h"
|
||||
#include "settings.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "Cura.pb.h"
|
||||
|
||||
namespace cura {
|
||||
|
||||
class fffProcessor;
|
||||
class CommandSocket
|
||||
{
|
||||
public:
|
||||
CommandSocket(fffProcessor* processor);
|
||||
|
||||
void connect(const std::string& ip, int port);
|
||||
|
||||
void handleObjectList(Cura::ObjectList* list);
|
||||
void handleSettingList(Cura::SettingList* list);
|
||||
|
||||
void sendLayerInfo(int layer_nr, int32_t z, int32_t height);
|
||||
void sendPolygons(cura::PolygonType type, int layer_nr, cura::Polygons& polygons, int line_width);
|
||||
void sendProgress(float amount);
|
||||
void sendPrintTime();
|
||||
void sendPrintMaterialForObject(int index, int extruder_nr, float material_amount);
|
||||
|
||||
void beginSendSlicedObject();
|
||||
void endSendSlicedObject();
|
||||
|
||||
void beginGCode();
|
||||
void sendGCodeLayer();
|
||||
void sendGCodePrefix(std::string prefix);
|
||||
|
||||
private:
|
||||
class Private;
|
||||
const std::unique_ptr<Private> d;
|
||||
};
|
||||
|
||||
}//namespace cura
|
||||
|
||||
#endif//COMMAND_SOCKET_H
|
||||
@@ -0,0 +1,61 @@
|
||||
#ifndef DEBUG_H
|
||||
#define DEBUG_H
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#define __FILE_NAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
|
||||
|
||||
|
||||
#define DEBUG_HERE std::cerr << __FILE_NAME__ << " : " << __LINE__ << std::endl
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#define DEBUG 1
|
||||
|
||||
#define DEBUG_SHOW_LINE 0
|
||||
|
||||
#if DEBUG_SHOW_LINE == 1
|
||||
#define DEBUG_FILE_LINE __FILE_NAME__ << "." << __LINE__ << ": "
|
||||
#else
|
||||
#define DEBUG_FILE_LINE ""
|
||||
#endif
|
||||
|
||||
#if DEBUG == 1
|
||||
# define DEBUG_DO(x) do { x } while (0)
|
||||
# define DEBUG_SHOW(x) do { std::cerr << DEBUG_FILE_LINE << #x << " = " << x << std::endl; } while (0)
|
||||
# define DEBUG_PRINTLN(x) do { std::cerr << DEBUG_FILE_LINE << x << std::endl; } while (0)
|
||||
#else
|
||||
# define DEBUG_DO(x)
|
||||
# define DEBUG_SHOW(x)
|
||||
# define DEBUG_PRINTLN(x)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#if 0==1
|
||||
#define ENUM(name, ...) enum class name { __VA_ARGS__, __COUNT};
|
||||
#endif
|
||||
#define ENUM(name, ...) enum class name { __VA_ARGS__}; \
|
||||
inline std::ostream& operator<<(std::ostream& os, name value) { \
|
||||
std::string enumName = #name; \
|
||||
std::string str = #__VA_ARGS__; \
|
||||
int len = str.length(); \
|
||||
std::vector<std::string> strings; \
|
||||
std::ostringstream temp; \
|
||||
for(int i = 0; i < len; i ++) { \
|
||||
if(isspace(str[i])) continue; \
|
||||
else if(str[i] == ',') { \
|
||||
strings.push_back(temp.str()); \
|
||||
temp.str(std::string());\
|
||||
} \
|
||||
else temp<< str[i]; \
|
||||
} \
|
||||
strings.push_back(temp.str()); \
|
||||
os << enumName << "::" << strings[static_cast<int>(value)]; \
|
||||
return os;}
|
||||
|
||||
#endif // DEBUG_H
|
||||
+882
-463
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+254
-473
@@ -1,67 +1,45 @@
|
||||
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <iomanip>
|
||||
|
||||
#include "gcodeExport.h"
|
||||
#include "pathOrderOptimizer.h"
|
||||
#include "timeEstimate.h"
|
||||
#include "settings.h"
|
||||
#include "utils/logoutput.h"
|
||||
|
||||
namespace cura {
|
||||
|
||||
GCodeExport::GCodeExport()
|
||||
: currentPosition(0,0,0)
|
||||
: output_stream(&std::cout), currentPosition(0,0,0), startPosition(INT32_MIN,INT32_MIN,0)
|
||||
{
|
||||
extrusionAmount = 0;
|
||||
extrusionPerMM = 0;
|
||||
retractionAmount = 4.5;
|
||||
minimalExtrusionBeforeRetraction = 0.0;
|
||||
extrusionAmountAtPreviousRetraction = -10000;
|
||||
extrusion_amount = 0;
|
||||
retraction_extrusion_window = 0.0;
|
||||
extruderSwitchRetraction = 14.5;
|
||||
extruderNr = 0;
|
||||
current_extruder = 0;
|
||||
currentFanSpeed = -1;
|
||||
|
||||
totalPrintTime = 0.0;
|
||||
for(unsigned int e=0; e<MAX_EXTRUDERS; e++)
|
||||
{
|
||||
totalFilament[e] = 0.0;
|
||||
currentTemperature[e] = 0;
|
||||
filament_diameter[e] = 0;
|
||||
}
|
||||
|
||||
currentSpeed = 0;
|
||||
retractionSpeed = 45;
|
||||
currentSpeed = 1;
|
||||
retractionPrimeSpeed = 1;
|
||||
isRetracted = false;
|
||||
isZHopped = false;
|
||||
setFlavor(GCODE_FLAVOR_REPRAP);
|
||||
memset(extruderOffset, 0, sizeof(extruderOffset));
|
||||
f = stdout;
|
||||
}
|
||||
|
||||
GCodeExport::~GCodeExport()
|
||||
{
|
||||
if (f && f != stdout)
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
void GCodeExport::replaceTagInStart(const char* tag, const char* replaceValue)
|
||||
void GCodeExport::setOutputStream(std::ostream* stream)
|
||||
{
|
||||
if (f == stdout)
|
||||
{
|
||||
cura::log("Replace:%s:%s\n", tag, replaceValue);
|
||||
return;
|
||||
}
|
||||
fpos_t oldPos;
|
||||
fgetpos(f, &oldPos);
|
||||
|
||||
char buffer[1024];
|
||||
fseek(f, 0, SEEK_SET);
|
||||
fread(buffer, 1024, 1, f);
|
||||
|
||||
char* c = strstr(buffer, tag);
|
||||
memset(c, ' ', strlen(tag));
|
||||
if (c) memcpy(c, replaceValue, strlen(replaceValue));
|
||||
|
||||
fseek(f, 0, SEEK_SET);
|
||||
fwrite(buffer, 1024, 1, f);
|
||||
|
||||
fsetpos(f, &oldPos);
|
||||
output_stream = stream;
|
||||
*output_stream << std::fixed;
|
||||
}
|
||||
|
||||
void GCodeExport::setExtruderOffset(int id, Point p)
|
||||
@@ -69,13 +47,18 @@ void GCodeExport::setExtruderOffset(int id, Point p)
|
||||
extruderOffset[id] = p;
|
||||
}
|
||||
|
||||
void GCodeExport::setSwitchExtruderCode(std::string preSwitchExtruderCode, std::string postSwitchExtruderCode)
|
||||
Point GCodeExport::getExtruderOffset(int id)
|
||||
{
|
||||
this->preSwitchExtruderCode = preSwitchExtruderCode;
|
||||
this->postSwitchExtruderCode = postSwitchExtruderCode;
|
||||
return extruderOffset[id];
|
||||
}
|
||||
|
||||
void GCodeExport::setFlavor(int flavor)
|
||||
void GCodeExport::setSwitchExtruderCode(int id, std::string preSwitchExtruderCode, std::string postSwitchExtruderCode)
|
||||
{
|
||||
this->preSwitchExtruderCode[id] = preSwitchExtruderCode;
|
||||
this->postSwitchExtruderCode[id] = postSwitchExtruderCode;
|
||||
}
|
||||
|
||||
void GCodeExport::setFlavor(EGCodeFlavor flavor)
|
||||
{
|
||||
this->flavor = flavor;
|
||||
if (flavor == GCODE_FLAVOR_MACH3)
|
||||
@@ -84,39 +67,28 @@ void GCodeExport::setFlavor(int flavor)
|
||||
else
|
||||
for(int n=0; n<MAX_EXTRUDERS; n++)
|
||||
extruderCharacter[n] = 'E';
|
||||
if (flavor == GCODE_FLAVOR_ULTIGCODE || flavor == GCODE_FLAVOR_REPRAP_VOLUMATRIC)
|
||||
{
|
||||
is_volumatric = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
is_volumatric = false;
|
||||
}
|
||||
}
|
||||
int GCodeExport::getFlavor()
|
||||
|
||||
EGCodeFlavor GCodeExport::getFlavor()
|
||||
{
|
||||
return this->flavor;
|
||||
}
|
||||
|
||||
void GCodeExport::setFilename(const char* filename)
|
||||
void GCodeExport::setRetractionSettings(int extruderSwitchRetraction, int extruderSwitchRetractionSpeed, int extruderSwitchPrimeSpeed, int retraction_extrusion_window, int retraction_count_max)
|
||||
{
|
||||
f = fopen(filename, "w+");
|
||||
}
|
||||
|
||||
bool GCodeExport::isOpened()
|
||||
{
|
||||
return f != nullptr;
|
||||
}
|
||||
|
||||
void GCodeExport::setExtrusion(int layerThickness, int filamentDiameter, int flow)
|
||||
{
|
||||
double filamentArea = M_PI * (INT2MM(filamentDiameter) / 2.0) * (INT2MM(filamentDiameter) / 2.0);
|
||||
if (flavor == GCODE_FLAVOR_ULTIGCODE || flavor == GCODE_FLAVOR_REPRAP_VOLUMATRIC)//UltiGCode uses volume extrusion as E value, and thus does not need the filamentArea in the mix.
|
||||
extrusionPerMM = INT2MM(layerThickness);
|
||||
else
|
||||
extrusionPerMM = INT2MM(layerThickness) / filamentArea * double(flow) / 100.0;
|
||||
}
|
||||
|
||||
void GCodeExport::setRetractionSettings(int retractionAmount, int retractionSpeed, int extruderSwitchRetraction, int minimalExtrusionBeforeRetraction, int zHop, int retractionAmountPrime)
|
||||
{
|
||||
this->retractionAmount = INT2MM(retractionAmount);
|
||||
this->retractionAmountPrime = INT2MM(retractionAmountPrime);
|
||||
this->retractionSpeed = retractionSpeed;
|
||||
this->extruderSwitchRetraction = INT2MM(extruderSwitchRetraction);
|
||||
this->minimalExtrusionBeforeRetraction = INT2MM(minimalExtrusionBeforeRetraction);
|
||||
this->retractionZHop = zHop;
|
||||
this->extruderSwitchRetractionSpeed = extruderSwitchRetractionSpeed;
|
||||
this->extruderSwitchPrimeSpeed = extruderSwitchPrimeSpeed;
|
||||
this->retraction_extrusion_window = INT2MM(retraction_extrusion_window);
|
||||
this->retraction_count_max = retraction_count_max;
|
||||
}
|
||||
|
||||
void GCodeExport::setZ(int z)
|
||||
@@ -124,6 +96,10 @@ void GCodeExport::setZ(int z)
|
||||
this->zPos = z;
|
||||
}
|
||||
|
||||
Point3 GCodeExport::getPosition()
|
||||
{
|
||||
return currentPosition;
|
||||
}
|
||||
Point GCodeExport::getPositionXY()
|
||||
{
|
||||
return Point(currentPosition.x, currentPosition.y);
|
||||
@@ -134,15 +110,51 @@ int GCodeExport::getPositionZ()
|
||||
return currentPosition.z;
|
||||
}
|
||||
|
||||
void GCodeExport::resetStartPosition()
|
||||
{
|
||||
startPosition.x = INT32_MIN;
|
||||
startPosition.y = INT32_MIN;
|
||||
}
|
||||
|
||||
Point GCodeExport::getStartPositionXY()
|
||||
{
|
||||
return Point(startPosition.x, startPosition.y);
|
||||
}
|
||||
|
||||
int GCodeExport::getExtruderNr()
|
||||
{
|
||||
return extruderNr;
|
||||
return current_extruder;
|
||||
}
|
||||
|
||||
double GCodeExport::getFilamentArea(unsigned int extruder)
|
||||
{
|
||||
double r = INT2MM(filament_diameter[extruder]) / 2.0;
|
||||
double filament_area = M_PI * r * r;
|
||||
return filament_area;
|
||||
}
|
||||
|
||||
void GCodeExport::setFilamentDiameter(unsigned int n, int diameter)
|
||||
{
|
||||
filament_diameter[n] = diameter;
|
||||
}
|
||||
|
||||
double GCodeExport::getExtrusionAmountMM3(unsigned int extruder)
|
||||
{
|
||||
if (is_volumatric)
|
||||
{
|
||||
return extrusion_amount;
|
||||
}
|
||||
else
|
||||
{
|
||||
return extrusion_amount * getFilamentArea(extruder);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
double GCodeExport::getTotalFilamentUsed(int e)
|
||||
{
|
||||
if (e == extruderNr)
|
||||
return totalFilament[e] + extrusionAmount;
|
||||
if (e == current_extruder)
|
||||
return totalFilament[e] + getExtrusionAmountMM3(e);
|
||||
return totalFilament[e];
|
||||
}
|
||||
|
||||
@@ -151,64 +163,86 @@ double GCodeExport::getTotalPrintTime()
|
||||
return totalPrintTime;
|
||||
}
|
||||
|
||||
void GCodeExport::resetTotalPrintTimeAndFilament()
|
||||
{
|
||||
totalPrintTime = 0;
|
||||
for(unsigned int e=0; e<MAX_EXTRUDERS; e++)
|
||||
{
|
||||
totalFilament[e] = 0.0;
|
||||
currentTemperature[e] = 0;
|
||||
}
|
||||
extrusion_amount = 0.0;
|
||||
estimateCalculator.reset();
|
||||
}
|
||||
|
||||
void GCodeExport::updateTotalPrintTime()
|
||||
{
|
||||
totalPrintTime += estimateCalculator.calculate();
|
||||
estimateCalculator.reset();
|
||||
}
|
||||
|
||||
void GCodeExport::writeComment(const char* comment, ...)
|
||||
void GCodeExport::writeComment(std::string comment)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, comment);
|
||||
fprintf(f, ";");
|
||||
vfprintf(f, comment, args);
|
||||
if (flavor == GCODE_FLAVOR_BFB)
|
||||
fprintf(f, "\r\n");
|
||||
else
|
||||
fprintf(f, "\n");
|
||||
va_end(args);
|
||||
*output_stream << ";" << comment << "\n";
|
||||
}
|
||||
|
||||
void GCodeExport::writeLine(const char* line, ...)
|
||||
void GCodeExport::writeTypeComment(const char* type)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, line);
|
||||
vfprintf(f, line, args);
|
||||
if (flavor == GCODE_FLAVOR_BFB)
|
||||
fprintf(f, "\r\n");
|
||||
else
|
||||
fprintf(f, "\n");
|
||||
va_end(args);
|
||||
*output_stream << ";TYPE:" << type << "\n";
|
||||
}
|
||||
void GCodeExport::writeLayerComment(int layer_nr)
|
||||
{
|
||||
*output_stream << ";LAYER:" << layer_nr << "\n";
|
||||
}
|
||||
|
||||
void GCodeExport::writeLine(const char* line)
|
||||
{
|
||||
*output_stream << line << "\n";
|
||||
}
|
||||
|
||||
void GCodeExport::resetExtrusionValue()
|
||||
{
|
||||
if (extrusionAmount != 0.0 && flavor != GCODE_FLAVOR_MAKERBOT && flavor != GCODE_FLAVOR_BFB)
|
||||
if (extrusion_amount != 0.0 && flavor != GCODE_FLAVOR_MAKERBOT && flavor != GCODE_FLAVOR_BFB)
|
||||
{
|
||||
fprintf(f, "G92 %c0\n", extruderCharacter[extruderNr]);
|
||||
totalFilament[extruderNr] += extrusionAmount;
|
||||
extrusionAmountAtPreviousRetraction -= extrusionAmount;
|
||||
extrusionAmount = 0.0;
|
||||
*output_stream << "G92 " << extruderCharacter[current_extruder] << "0\n";
|
||||
totalFilament[current_extruder] += getExtrusionAmountMM3(current_extruder);
|
||||
for (unsigned int i = 0; i < extrusion_amount_at_previous_n_retractions.size(); i++)
|
||||
extrusion_amount_at_previous_n_retractions[i] -= extrusion_amount;
|
||||
extrusion_amount = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
void GCodeExport::writeDelay(double timeAmount)
|
||||
{
|
||||
fprintf(f, "G4 P%d\n", int(timeAmount * 1000));
|
||||
*output_stream << "G4 P" << int(timeAmount * 1000) << "\n";
|
||||
totalPrintTime += timeAmount;
|
||||
}
|
||||
|
||||
void GCodeExport::writeMove(Point p, int speed, int lineWidth)
|
||||
void GCodeExport::writeMove(Point p, int speed, double extrusion_mm3_per_mm)
|
||||
{
|
||||
if (currentPosition.x == p.X && currentPosition.y == p.Y && currentPosition.z == zPos)
|
||||
writeMove(p.X, p.Y, zPos, speed, extrusion_mm3_per_mm);
|
||||
}
|
||||
|
||||
void GCodeExport::writeMove(Point3 p, int speed, double extrusion_mm3_per_mm)
|
||||
{
|
||||
writeMove(p.x, p.y, p.z, speed, extrusion_mm3_per_mm);
|
||||
}
|
||||
void GCodeExport::writeMove(int x, int y, int z, int speed, double extrusion_mm3_per_mm)
|
||||
{
|
||||
if (currentPosition.x == x && currentPosition.y == y && currentPosition.z == z)
|
||||
return;
|
||||
|
||||
double extrusion_per_mm = extrusion_mm3_per_mm;
|
||||
if (!is_volumatric)
|
||||
{
|
||||
extrusion_per_mm = extrusion_mm3_per_mm / getFilamentArea(current_extruder);
|
||||
}
|
||||
|
||||
if (flavor == GCODE_FLAVOR_BFB)
|
||||
{
|
||||
//For Bits From Bytes machines, we need to handle this completely differently. As they do not use E values but RPM values.
|
||||
float fspeed = speed * 60;
|
||||
float rpm = (extrusionPerMM * double(lineWidth) / 1000.0) * speed * 60;
|
||||
float rpm = extrusion_per_mm * speed * 60;
|
||||
const float mm_per_rpm = 4.0; //All BFB machines have 4mm per RPM extrusion.
|
||||
rpm /= mm_per_rpm;
|
||||
if (rpm > 0)
|
||||
@@ -217,11 +251,13 @@ void GCodeExport::writeMove(Point p, int speed, int lineWidth)
|
||||
{
|
||||
if (currentSpeed != int(rpm * 10))
|
||||
{
|
||||
//fprintf(f, "; %f e-per-mm %d mm-width %d mm/s\n", extrusionPerMM, lineWidth, speed);
|
||||
fprintf(f, "M108 S%0.1f\r\n", rpm);
|
||||
//fprintf(f, "; %f e-per-mm %d mm-width %d mm/s\n", extrusion_per_mm, lineWidth, speed);
|
||||
//fprintf(f, "M108 S%0.1f\r\n", rpm);
|
||||
*output_stream << "M108 S" << std::setprecision(1) << rpm << "\r\n";
|
||||
currentSpeed = int(rpm * 10);
|
||||
}
|
||||
fprintf(f, "M%d01\r\n", extruderNr + 1);
|
||||
//Add M101 or M201 to enable the proper extruder.
|
||||
*output_stream << "M" << int((current_extruder + 1) * 100 + 1) << "\r\n";
|
||||
isRetracted = false;
|
||||
}
|
||||
//Fix the speed by the actual RPM we are asking, because of rounding errors we cannot get all RPM values, but we have a lot more resolution in the feedrate value.
|
||||
@@ -229,94 +265,120 @@ void GCodeExport::writeMove(Point p, int speed, int lineWidth)
|
||||
fspeed *= (rpm / (roundf(rpm * 100) / 100));
|
||||
|
||||
//Increase the extrusion amount to calculate the amount of filament used.
|
||||
Point diff = p - getPositionXY();
|
||||
extrusionAmount += extrusionPerMM * INT2MM(lineWidth) * vSizeMM(diff);
|
||||
Point3 diff = Point3(x,y,z) - getPosition();
|
||||
|
||||
extrusion_amount += extrusion_per_mm * diff.vSizeMM();
|
||||
}else{
|
||||
//If we are not extruding, check if we still need to disable the extruder. This causes a retraction due to auto-retraction.
|
||||
if (!isRetracted)
|
||||
{
|
||||
fprintf(f, "M103\r\n");
|
||||
*output_stream << "M103\r\n";
|
||||
isRetracted = true;
|
||||
}
|
||||
}
|
||||
fprintf(f, "G1 X%0.3f Y%0.3f Z%0.3f F%0.1f\r\n", INT2MM(p.X - extruderOffset[extruderNr].X), INT2MM(p.Y - extruderOffset[extruderNr].Y), INT2MM(zPos), fspeed);
|
||||
*output_stream << std::setprecision(3) << "G1 X" << INT2MM(x - extruderOffset[current_extruder].X) << " Y" << INT2MM(y - extruderOffset[current_extruder].Y) << " Z" << INT2MM(z) << std::setprecision(1) << " F" << fspeed << "\r\n";
|
||||
}else{
|
||||
|
||||
//Normal E handling.
|
||||
if (lineWidth != 0)
|
||||
if (extrusion_mm3_per_mm > 0.000001)
|
||||
{
|
||||
Point diff = p - getPositionXY();
|
||||
Point3 diff = Point3(x,y,z) - getPosition();
|
||||
if (isZHopped > 0)
|
||||
{
|
||||
*output_stream << std::setprecision(3) << "G1 Z" << INT2MM(currentPosition.z) << "\n";
|
||||
isZHopped = false;
|
||||
}
|
||||
if (isRetracted)
|
||||
{
|
||||
if (retractionZHop > 0)
|
||||
fprintf(f, "G1 Z%0.3f\n", float(currentPosition.z)/1000);
|
||||
if (flavor == GCODE_FLAVOR_ULTIGCODE || flavor == GCODE_FLAVOR_REPRAP_VOLUMATRIC)
|
||||
{
|
||||
fprintf(f, "G11\n");
|
||||
*output_stream << "G11\n";
|
||||
//Assume default UM2 retraction settings.
|
||||
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), extrusion_amount), 25.0);
|
||||
}else{
|
||||
extrusionAmount += retractionAmountPrime;
|
||||
fprintf(f, "G1 F%i %c%0.5f\n", retractionSpeed * 60, extruderCharacter[extruderNr], extrusionAmount);
|
||||
currentSpeed = retractionSpeed;
|
||||
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), extrusionAmount), currentSpeed);
|
||||
*output_stream << "G1 F" << (retractionPrimeSpeed * 60) << " " << extruderCharacter[current_extruder] << std::setprecision(5) << extrusion_amount << "\n";
|
||||
currentSpeed = retractionPrimeSpeed;
|
||||
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), extrusion_amount), currentSpeed);
|
||||
}
|
||||
if (extrusionAmount > 10000.0) //According to https://github.com/Ultimaker/CuraEngine/issues/14 having more then 21m of extrusion causes inaccuracies. So reset it every 10m, just to be sure.
|
||||
if (getExtrusionAmountMM3(current_extruder) > 10000.0) //According to https://github.com/Ultimaker/CuraEngine/issues/14 having more then 21m of extrusion causes inaccuracies. So reset it every 10m, just to be sure.
|
||||
resetExtrusionValue();
|
||||
isRetracted = false;
|
||||
}
|
||||
extrusionAmount += extrusionPerMM * INT2MM(lineWidth) * vSizeMM(diff);
|
||||
fprintf(f, "G1");
|
||||
extrusion_amount += extrusion_per_mm * diff.vSizeMM();
|
||||
*output_stream << "G1";
|
||||
}else{
|
||||
fprintf(f, "G0");
|
||||
*output_stream << "G0";
|
||||
}
|
||||
|
||||
if (currentSpeed != speed)
|
||||
{
|
||||
fprintf(f, " F%i", speed * 60);
|
||||
*output_stream << " F" << (speed * 60);
|
||||
currentSpeed = speed;
|
||||
}
|
||||
|
||||
fprintf(f, " X%0.3f Y%0.3f", INT2MM(p.X - extruderOffset[extruderNr].X), INT2MM(p.Y - extruderOffset[extruderNr].Y));
|
||||
if (zPos != currentPosition.z)
|
||||
fprintf(f, " Z%0.3f", INT2MM(zPos));
|
||||
if (lineWidth != 0)
|
||||
fprintf(f, " %c%0.5f", extruderCharacter[extruderNr], extrusionAmount);
|
||||
fprintf(f, "\n");
|
||||
*output_stream << std::setprecision(3) << " X" << INT2MM(x - extruderOffset[current_extruder].X) << " Y" << INT2MM(y - extruderOffset[current_extruder].Y);
|
||||
if (z != currentPosition.z)
|
||||
*output_stream << " Z" << INT2MM(z);
|
||||
if (extrusion_mm3_per_mm > 0.000001)
|
||||
*output_stream << " " << extruderCharacter[current_extruder] << std::setprecision(5) << extrusion_amount;
|
||||
*output_stream << "\n";
|
||||
}
|
||||
|
||||
currentPosition = Point3(p.X, p.Y, zPos);
|
||||
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), extrusionAmount), speed);
|
||||
currentPosition = Point3(x, y, z);
|
||||
startPosition = currentPosition;
|
||||
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), extrusion_amount), speed);
|
||||
}
|
||||
|
||||
void GCodeExport::writeRetraction(bool force)
|
||||
void GCodeExport::writeRetraction(RetractionConfig* config, bool force)
|
||||
{
|
||||
if (flavor == GCODE_FLAVOR_BFB)//BitsFromBytes does automatic retraction.
|
||||
return;
|
||||
if (isRetracted)
|
||||
return;
|
||||
if (config->amount <= 0)
|
||||
return;
|
||||
|
||||
if (retractionAmount > 0 && !isRetracted && (extrusionAmountAtPreviousRetraction + minimalExtrusionBeforeRetraction < extrusionAmount || force))
|
||||
if (!force && retraction_count_max > 0 && int(extrusion_amount_at_previous_n_retractions.size()) == retraction_count_max - 1
|
||||
&& extrusion_amount < extrusion_amount_at_previous_n_retractions.back() + retraction_extrusion_window)
|
||||
return;
|
||||
|
||||
if (config->primeAmount > 0)
|
||||
extrusion_amount += config->primeAmount;
|
||||
retractionPrimeSpeed = config->primeSpeed;
|
||||
|
||||
if (flavor == GCODE_FLAVOR_ULTIGCODE || flavor == GCODE_FLAVOR_REPRAP_VOLUMATRIC)
|
||||
{
|
||||
if (flavor == GCODE_FLAVOR_ULTIGCODE || flavor == GCODE_FLAVOR_REPRAP_VOLUMATRIC)
|
||||
{
|
||||
fprintf(f, "G10\n");
|
||||
}else{
|
||||
fprintf(f, "G1 F%i %c%0.5f\n", retractionSpeed * 60, extruderCharacter[extruderNr], extrusionAmount - retractionAmount);
|
||||
currentSpeed = retractionSpeed;
|
||||
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), extrusionAmount - retractionAmount), currentSpeed);
|
||||
}
|
||||
if (retractionZHop > 0)
|
||||
fprintf(f, "G1 Z%0.3f\n", INT2MM(currentPosition.z + retractionZHop));
|
||||
extrusionAmountAtPreviousRetraction = extrusionAmount;
|
||||
isRetracted = true;
|
||||
*output_stream << "G10\n";
|
||||
//Assume default UM2 retraction settings.
|
||||
double retraction_distance = 4.5;
|
||||
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), extrusion_amount - retraction_distance), 25); // TODO: hardcoded values!
|
||||
}else{
|
||||
*output_stream << "G1 F" << (config->speed * 60) << " " << extruderCharacter[current_extruder] << std::setprecision(5) << extrusion_amount - config->amount << "\n";
|
||||
currentSpeed = config->speed;
|
||||
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), extrusion_amount - config->amount), currentSpeed);
|
||||
}
|
||||
if (config->zHop > 0)
|
||||
{
|
||||
*output_stream << std::setprecision(3) << "G1 Z" << INT2MM(currentPosition.z + config->zHop) << "\n";
|
||||
isZHopped = true;
|
||||
}
|
||||
extrusion_amount_at_previous_n_retractions.push_front(extrusion_amount);
|
||||
if (int(extrusion_amount_at_previous_n_retractions.size()) == retraction_count_max)
|
||||
{
|
||||
extrusion_amount_at_previous_n_retractions.pop_back();
|
||||
}
|
||||
isRetracted = true;
|
||||
}
|
||||
|
||||
void GCodeExport::switchExtruder(int newExtruder)
|
||||
{
|
||||
if (extruderNr == newExtruder)
|
||||
if (current_extruder == newExtruder)
|
||||
return;
|
||||
|
||||
if (flavor == GCODE_FLAVOR_BFB)
|
||||
{
|
||||
if (!isRetracted)
|
||||
fprintf(f, "M103\r\n");
|
||||
*output_stream << "M103\r\n";
|
||||
|
||||
isRetracted = true;
|
||||
return;
|
||||
}
|
||||
@@ -324,32 +386,34 @@ void GCodeExport::switchExtruder(int newExtruder)
|
||||
resetExtrusionValue();
|
||||
if (flavor == GCODE_FLAVOR_ULTIGCODE || flavor == GCODE_FLAVOR_REPRAP_VOLUMATRIC)
|
||||
{
|
||||
fprintf(f, "G10 S1\n");
|
||||
*output_stream << "G10 S1\n";
|
||||
}else{
|
||||
fprintf(f, "G1 F%i %c%0.5f\n", retractionSpeed * 60, extruderCharacter[extruderNr], extrusionAmount - extruderSwitchRetraction);
|
||||
currentSpeed = retractionSpeed;
|
||||
*output_stream << "G1 F" << (extruderSwitchRetractionSpeed * 60) << " " << extruderCharacter[current_extruder] << std::setprecision(5) << (extrusion_amount - extruderSwitchRetraction) << "\n";
|
||||
currentSpeed = extruderSwitchRetractionSpeed;
|
||||
}
|
||||
if (retractionZHop > 0)
|
||||
fprintf(f, "G1 Z%0.3f\n", INT2MM(currentPosition.z + retractionZHop));
|
||||
extruderNr = newExtruder;
|
||||
|
||||
current_extruder = newExtruder;
|
||||
if (flavor == GCODE_FLAVOR_MACH3)
|
||||
resetExtrusionValue();
|
||||
isRetracted = true;
|
||||
writeCode(preSwitchExtruderCode.c_str());
|
||||
writeCode(preSwitchExtruderCode[current_extruder].c_str());
|
||||
if (flavor == GCODE_FLAVOR_MAKERBOT)
|
||||
fprintf(f, "M135 T%i\n", extruderNr);
|
||||
*output_stream << "M135 T" << current_extruder << "\n";
|
||||
else
|
||||
fprintf(f, "T%i\n", extruderNr);
|
||||
writeCode(postSwitchExtruderCode.c_str());
|
||||
*output_stream << "T" << current_extruder << "\n";
|
||||
writeCode(postSwitchExtruderCode[current_extruder].c_str());
|
||||
|
||||
//Change the Z position so it gets re-writting again. We do not know if the switch code modified the Z position.
|
||||
currentPosition.z += 1;
|
||||
}
|
||||
|
||||
void GCodeExport::writeCode(const char* str)
|
||||
{
|
||||
fprintf(f, "%s", str);
|
||||
*output_stream << str;
|
||||
if (flavor == GCODE_FLAVOR_BFB)
|
||||
fprintf(f, "\r\n");
|
||||
*output_stream << "\r\n";
|
||||
else
|
||||
fprintf(f, "\n");
|
||||
*output_stream << "\n";
|
||||
}
|
||||
|
||||
void GCodeExport::writeFanCommand(int speed)
|
||||
@@ -359,340 +423,57 @@ void GCodeExport::writeFanCommand(int speed)
|
||||
if (speed > 0)
|
||||
{
|
||||
if (flavor == GCODE_FLAVOR_MAKERBOT)
|
||||
fprintf(f, "M126 T0 ; value = %d\n", speed * 255 / 100);
|
||||
*output_stream << "M126 T0\n"; //value = speed * 255 / 100 // Makerbot cannot set fan speed...;
|
||||
else
|
||||
fprintf(f, "M106 S%d\n", speed * 255 / 100);
|
||||
*output_stream << "M106 S" << (speed * 255 / 100) << "\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (flavor == GCODE_FLAVOR_MAKERBOT)
|
||||
fprintf(f, "M127 T0\n");
|
||||
*output_stream << "M127 T0\n";
|
||||
else
|
||||
fprintf(f, "M107\n");
|
||||
*output_stream << "M107\n";
|
||||
}
|
||||
currentFanSpeed = speed;
|
||||
}
|
||||
|
||||
int GCodeExport::getFileSize(){
|
||||
return ftell(f);
|
||||
void GCodeExport::writeTemperatureCommand(int extruder, int temperature, bool wait)
|
||||
{
|
||||
if (!wait && currentTemperature[extruder] == temperature)
|
||||
return;
|
||||
|
||||
if (wait)
|
||||
*output_stream << "M109";
|
||||
else
|
||||
*output_stream << "M104";
|
||||
if (extruder != current_extruder)
|
||||
*output_stream << " T" << extruder;
|
||||
*output_stream << " S" << temperature << "\n";
|
||||
currentTemperature[extruder] = temperature;
|
||||
}
|
||||
void GCodeExport::tellFileSize() {
|
||||
float fsize = ftell(f);
|
||||
if(fsize > 1024*1024) {
|
||||
fsize /= 1024.0*1024.0;
|
||||
cura::log("Wrote %5.1f MB.\n",fsize);
|
||||
}
|
||||
if(fsize > 1024) {
|
||||
fsize /= 1024.0;
|
||||
cura::log("Wrote %5.1f kilobytes.\n",fsize);
|
||||
}
|
||||
|
||||
void GCodeExport::writeBedTemperatureCommand(int temperature, bool wait)
|
||||
{
|
||||
if (wait)
|
||||
*output_stream << "M190 S";
|
||||
else
|
||||
*output_stream << "M140 S";
|
||||
*output_stream << temperature << "\n";
|
||||
}
|
||||
|
||||
void GCodeExport::finalize(int maxObjectHeight, int moveSpeed, const char* endCode)
|
||||
{
|
||||
std::cerr << "maxObjectHeight : " << maxObjectHeight << std::endl;
|
||||
writeFanCommand(0);
|
||||
writeRetraction();
|
||||
setZ(maxObjectHeight + 5000);
|
||||
writeMove(getPositionXY(), moveSpeed, 0);
|
||||
writeMove(Point3(0,0,maxObjectHeight + 5000) + getPositionXY(), moveSpeed, 0);
|
||||
writeCode(endCode);
|
||||
cura::log("Print time: %d\n", int(getTotalPrintTime()));
|
||||
cura::log("Filament: %d\n", int(getTotalFilamentUsed(0)));
|
||||
cura::log("Filament2: %d\n", int(getTotalFilamentUsed(1)));
|
||||
|
||||
if (getFlavor() == GCODE_FLAVOR_ULTIGCODE)
|
||||
{
|
||||
char numberString[16];
|
||||
sprintf(numberString, "%d", int(getTotalPrintTime()));
|
||||
replaceTagInStart("<__TIME__>", numberString);
|
||||
sprintf(numberString, "%d", int(getTotalFilamentUsed(0)));
|
||||
replaceTagInStart("<FILAMENT>", numberString);
|
||||
sprintf(numberString, "%d", int(getTotalFilamentUsed(1)));
|
||||
replaceTagInStart("<FILAMEN2>", numberString);
|
||||
}
|
||||
}
|
||||
|
||||
GCodePath* GCodePlanner::getLatestPathWithConfig(GCodePathConfig* config)
|
||||
{
|
||||
if (paths.size() > 0 && paths[paths.size()-1].config == config && !paths[paths.size()-1].done)
|
||||
return &paths[paths.size()-1];
|
||||
paths.push_back(GCodePath());
|
||||
GCodePath* ret = &paths[paths.size()-1];
|
||||
ret->retract = false;
|
||||
ret->config = config;
|
||||
ret->extruder = currentExtruder;
|
||||
ret->done = false;
|
||||
return ret;
|
||||
}
|
||||
void GCodePlanner::forceNewPathStart()
|
||||
{
|
||||
if (paths.size() > 0)
|
||||
paths[paths.size()-1].done = true;
|
||||
}
|
||||
|
||||
GCodePlanner::GCodePlanner(GCodeExport& gcode, int travelSpeed, int retractionMinimalDistance)
|
||||
: gcode(gcode), travelConfig(travelSpeed, 0, "travel")
|
||||
{
|
||||
lastPosition = gcode.getPositionXY();
|
||||
comb = nullptr;
|
||||
extrudeSpeedFactor = 100;
|
||||
travelSpeedFactor = 100;
|
||||
extraTime = 0.0;
|
||||
totalPrintTime = 0.0;
|
||||
forceRetraction = false;
|
||||
alwaysRetract = false;
|
||||
currentExtruder = gcode.getExtruderNr();
|
||||
this->retractionMinimalDistance = retractionMinimalDistance;
|
||||
}
|
||||
GCodePlanner::~GCodePlanner()
|
||||
{
|
||||
if (comb)
|
||||
delete comb;
|
||||
}
|
||||
|
||||
void GCodePlanner::addTravel(Point p)
|
||||
{
|
||||
GCodePath* path = getLatestPathWithConfig(&travelConfig);
|
||||
if (forceRetraction)
|
||||
{
|
||||
if (!shorterThen(lastPosition - p, retractionMinimalDistance))
|
||||
{
|
||||
path->retract = true;
|
||||
}
|
||||
forceRetraction = false;
|
||||
}else if (comb != nullptr)
|
||||
{
|
||||
vector<Point> pointList;
|
||||
if (comb->calc(lastPosition, p, pointList))
|
||||
{
|
||||
for(unsigned int n=0; n<pointList.size(); n++)
|
||||
{
|
||||
path->points.push_back(pointList[n]);
|
||||
}
|
||||
}else{
|
||||
if (!shorterThen(lastPosition - p, retractionMinimalDistance))
|
||||
path->retract = true;
|
||||
}
|
||||
}else if (alwaysRetract)
|
||||
{
|
||||
if (!shorterThen(lastPosition - p, retractionMinimalDistance))
|
||||
path->retract = true;
|
||||
}
|
||||
path->points.push_back(p);
|
||||
lastPosition = p;
|
||||
}
|
||||
|
||||
void GCodePlanner::addExtrusionMove(Point p, GCodePathConfig* config)
|
||||
{
|
||||
getLatestPathWithConfig(config)->points.push_back(p);
|
||||
lastPosition = p;
|
||||
}
|
||||
|
||||
void GCodePlanner::moveInsideCombBoundary(int distance)
|
||||
{
|
||||
if (!comb || comb->inside(lastPosition)) return;
|
||||
Point p = lastPosition;
|
||||
if (comb->moveInside(&p, distance))
|
||||
{
|
||||
//Move inside again, so we move out of tight 90deg corners
|
||||
comb->moveInside(&p, distance);
|
||||
if (comb->inside(p))
|
||||
{
|
||||
addTravel(p);
|
||||
//Make sure the that any retraction happens after this move, not before it by starting a new move path.
|
||||
forceNewPathStart();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GCodePlanner::addPolygon(PolygonRef polygon, int startIdx, GCodePathConfig* config)
|
||||
{
|
||||
Point p0 = polygon[startIdx];
|
||||
addTravel(p0);
|
||||
for(unsigned int i=1; i<polygon.size(); i++)
|
||||
{
|
||||
Point p1 = polygon[(startIdx + i) % polygon.size()];
|
||||
addExtrusionMove(p1, config);
|
||||
p0 = p1;
|
||||
}
|
||||
if (polygon.size() > 2)
|
||||
addExtrusionMove(polygon[startIdx], config);
|
||||
}
|
||||
|
||||
void GCodePlanner::addPolygonsByOptimizer(Polygons& polygons, GCodePathConfig* config)
|
||||
{
|
||||
PathOrderOptimizer orderOptimizer(lastPosition);
|
||||
for(unsigned int i=0;i<polygons.size();i++)
|
||||
orderOptimizer.addPolygon(polygons[i]);
|
||||
orderOptimizer.optimize();
|
||||
for(unsigned int i=0;i<orderOptimizer.polyOrder.size();i++)
|
||||
{
|
||||
int nr = orderOptimizer.polyOrder[i];
|
||||
addPolygon(polygons[nr], orderOptimizer.polyStart[nr], config);
|
||||
}
|
||||
}
|
||||
|
||||
void GCodePlanner::forceMinimalLayerTime(double minTime, int minimalSpeed)
|
||||
{
|
||||
Point p0 = gcode.getPositionXY();
|
||||
double travelTime = 0.0;
|
||||
double extrudeTime = 0.0;
|
||||
for(unsigned int n=0; n<paths.size(); n++)
|
||||
{
|
||||
GCodePath* path = &paths[n];
|
||||
for(unsigned int i=0; i<path->points.size(); i++)
|
||||
{
|
||||
double thisTime = vSizeMM(p0 - path->points[i]) / double(path->config->speed);
|
||||
if (path->config->lineWidth != 0)
|
||||
extrudeTime += thisTime;
|
||||
else
|
||||
travelTime += thisTime;
|
||||
p0 = path->points[i];
|
||||
}
|
||||
}
|
||||
double totalTime = extrudeTime + travelTime;
|
||||
if (totalTime < minTime && extrudeTime > 0.0)
|
||||
{
|
||||
double minExtrudeTime = minTime - travelTime;
|
||||
if (minExtrudeTime < 1)
|
||||
minExtrudeTime = 1;
|
||||
double factor = extrudeTime / minExtrudeTime;
|
||||
for(unsigned int n=0; n<paths.size(); n++)
|
||||
{
|
||||
GCodePath* path = &paths[n];
|
||||
if (path->config->lineWidth == 0)
|
||||
continue;
|
||||
int speed = path->config->speed * factor;
|
||||
if (speed < minimalSpeed)
|
||||
factor = double(minimalSpeed) / double(path->config->speed);
|
||||
}
|
||||
|
||||
//Only slow down with the minimal time if that will be slower then a factor already set. First layer slowdown also sets the speed factor.
|
||||
if (factor * 100 < getExtrudeSpeedFactor())
|
||||
setExtrudeSpeedFactor(factor * 100);
|
||||
else
|
||||
factor = getExtrudeSpeedFactor() / 100.0;
|
||||
|
||||
if (minTime - (extrudeTime / factor) - travelTime > 0.1)
|
||||
{
|
||||
//TODO: Use up this extra time (circle around the print?)
|
||||
this->extraTime = minTime - (extrudeTime / factor) - travelTime;
|
||||
}
|
||||
this->totalPrintTime = (extrudeTime / factor) + travelTime;
|
||||
}else{
|
||||
this->totalPrintTime = totalTime;
|
||||
}
|
||||
}
|
||||
|
||||
void GCodePlanner::writeGCode(bool liftHeadIfNeeded, int layerThickness)
|
||||
{
|
||||
GCodePathConfig* lastConfig = nullptr;
|
||||
int extruder = gcode.getExtruderNr();
|
||||
|
||||
for(unsigned int n=0; n<paths.size(); n++)
|
||||
{
|
||||
GCodePath* path = &paths[n];
|
||||
if (extruder != path->extruder)
|
||||
{
|
||||
extruder = path->extruder;
|
||||
gcode.switchExtruder(extruder);
|
||||
}else if (path->retract)
|
||||
{
|
||||
gcode.writeRetraction();
|
||||
}
|
||||
if (path->config != &travelConfig && lastConfig != path->config)
|
||||
{
|
||||
gcode.writeComment("TYPE:%s", path->config->name);
|
||||
lastConfig = path->config;
|
||||
}
|
||||
int speed = path->config->speed;
|
||||
|
||||
if (path->config->lineWidth != 0)// Only apply the extrudeSpeedFactor to extrusion moves
|
||||
speed = speed * extrudeSpeedFactor / 100;
|
||||
else
|
||||
speed = speed * travelSpeedFactor / 100;
|
||||
|
||||
if (path->points.size() == 1 && path->config != &travelConfig && shorterThen(gcode.getPositionXY() - path->points[0], path->config->lineWidth * 2))
|
||||
{
|
||||
//Check for lots of small moves and combine them into one large line
|
||||
Point p0 = path->points[0];
|
||||
unsigned int i = n + 1;
|
||||
while(i < paths.size() && paths[i].points.size() == 1 && shorterThen(p0 - paths[i].points[0], path->config->lineWidth * 2))
|
||||
{
|
||||
p0 = paths[i].points[0];
|
||||
i ++;
|
||||
}
|
||||
if (paths[i-1].config == &travelConfig)
|
||||
i --;
|
||||
if (i > n + 2)
|
||||
{
|
||||
p0 = gcode.getPositionXY();
|
||||
for(unsigned int x=n; x<i-1; x+=2)
|
||||
{
|
||||
int64_t oldLen = vSize(p0 - paths[x].points[0]);
|
||||
Point newPoint = (paths[x].points[0] + paths[x+1].points[0]) / 2;
|
||||
int64_t newLen = vSize(gcode.getPositionXY() - newPoint);
|
||||
if (newLen > 0)
|
||||
gcode.writeMove(newPoint, speed, path->config->lineWidth * oldLen / newLen);
|
||||
|
||||
p0 = paths[x+1].points[0];
|
||||
}
|
||||
gcode.writeMove(paths[i-1].points[0], speed, path->config->lineWidth);
|
||||
n = i - 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
bool spiralize = path->config->spiralize;
|
||||
if (spiralize)
|
||||
{
|
||||
//Check if we are the last spiralize path in the list, if not, do not spiralize.
|
||||
for(unsigned int m=n+1; m<paths.size(); m++)
|
||||
{
|
||||
if (paths[m].config->spiralize)
|
||||
spiralize = false;
|
||||
}
|
||||
}
|
||||
if (spiralize)
|
||||
{
|
||||
//If we need to spiralize then raise the head slowly by 1 layer as this path progresses.
|
||||
float totalLength = 0.0;
|
||||
int z = gcode.getPositionZ();
|
||||
Point p0 = gcode.getPositionXY();
|
||||
for(unsigned int i=0; i<path->points.size(); i++)
|
||||
{
|
||||
Point p1 = path->points[i];
|
||||
totalLength += vSizeMM(p0 - p1);
|
||||
p0 = p1;
|
||||
}
|
||||
|
||||
float length = 0.0;
|
||||
p0 = gcode.getPositionXY();
|
||||
for(unsigned int i=0; i<path->points.size(); i++)
|
||||
{
|
||||
Point p1 = path->points[i];
|
||||
length += vSizeMM(p0 - p1);
|
||||
p0 = p1;
|
||||
gcode.setZ(z + layerThickness * length / totalLength);
|
||||
gcode.writeMove(path->points[i], speed, path->config->lineWidth);
|
||||
}
|
||||
}else{
|
||||
for(unsigned int i=0; i<path->points.size(); i++)
|
||||
{
|
||||
gcode.writeMove(path->points[i], speed, path->config->lineWidth);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gcode.updateTotalPrintTime();
|
||||
if (liftHeadIfNeeded && extraTime > 0.0)
|
||||
{
|
||||
gcode.writeComment("Small layer, adding delay of %f", extraTime);
|
||||
gcode.writeRetraction(true);
|
||||
gcode.setZ(gcode.getPositionZ() + MM2INT(3.0));
|
||||
gcode.writeMove(gcode.getPositionXY(), travelConfig.speed, 0);
|
||||
gcode.writeMove(gcode.getPositionXY() - Point(-MM2INT(20.0), 0), travelConfig.speed, 0);
|
||||
gcode.writeDelay(extraTime);
|
||||
}
|
||||
log("Print time: %d\n", int(getTotalPrintTime()));
|
||||
log("Filament: %d\n", int(getTotalFilamentUsed(0)));
|
||||
for(int n=1; n<MAX_EXTRUDERS; n++)
|
||||
if (getTotalFilamentUsed(n) > 0)
|
||||
log("Filament%d: %d\n", n + 1, int(getTotalFilamentUsed(n)));
|
||||
output_stream->flush();
|
||||
}
|
||||
|
||||
}//namespace cura
|
||||
|
||||
+132
-163
@@ -3,90 +3,184 @@
|
||||
#define GCODEEXPORT_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <deque> // for extrusionAmountAtPreviousRetractions
|
||||
|
||||
#include "settings.h"
|
||||
#include "comb.h"
|
||||
#include "utils/intpoint.h"
|
||||
#include "utils/polygon.h"
|
||||
#include "timeEstimate.h"
|
||||
|
||||
namespace cura {
|
||||
|
||||
class RetractionConfig
|
||||
{
|
||||
public:
|
||||
double amount; //!< The amount
|
||||
int speed;
|
||||
int primeSpeed;
|
||||
double primeAmount;
|
||||
int zHop;
|
||||
};
|
||||
|
||||
//The GCodePathConfig is the configuration for moves/extrusion actions. This defines at which width the line is printed and at which speed.
|
||||
class GCodePathConfig
|
||||
{
|
||||
private:
|
||||
int speed;
|
||||
int line_width;
|
||||
int flow;
|
||||
int layer_thickness;
|
||||
double extrusion_mm3_per_mm;
|
||||
public:
|
||||
const char* name;
|
||||
bool spiralize;
|
||||
RetractionConfig* retraction_config;
|
||||
|
||||
GCodePathConfig() : speed(0), line_width(0), extrusion_mm3_per_mm(0.0), name(nullptr), spiralize(false), retraction_config(nullptr) {}
|
||||
GCodePathConfig(RetractionConfig* retraction_config, const char* name) : speed(0), line_width(0), extrusion_mm3_per_mm(0.0), name(name), spiralize(false), retraction_config(retraction_config) {}
|
||||
|
||||
void setSpeed(int speed)
|
||||
{
|
||||
this->speed = speed;
|
||||
}
|
||||
|
||||
void setLineWidth(int line_width)
|
||||
{
|
||||
this->line_width = line_width;
|
||||
calculateExtrusion();
|
||||
}
|
||||
|
||||
void setLayerHeight(int layer_height)
|
||||
{
|
||||
this->layer_thickness = layer_height;
|
||||
calculateExtrusion();
|
||||
}
|
||||
|
||||
void setFlow(int flow)
|
||||
{
|
||||
this->flow = flow;
|
||||
calculateExtrusion();
|
||||
}
|
||||
|
||||
void smoothSpeed(int min_speed, int layer_nr, int max_speed_layer)
|
||||
{
|
||||
speed = (speed*layer_nr)/max_speed_layer + (min_speed*(max_speed_layer-layer_nr)/max_speed_layer);
|
||||
}
|
||||
|
||||
double getExtrusionMM3perMM()
|
||||
{
|
||||
return extrusion_mm3_per_mm;
|
||||
}
|
||||
|
||||
int getSpeed()
|
||||
{
|
||||
return speed;
|
||||
}
|
||||
|
||||
int getLineWidth()
|
||||
{
|
||||
return line_width;
|
||||
}
|
||||
|
||||
private:
|
||||
void calculateExtrusion()
|
||||
{
|
||||
extrusion_mm3_per_mm = INT2MM(line_width) * INT2MM(layer_thickness) * double(flow) / 100.0;
|
||||
}
|
||||
};
|
||||
|
||||
//The GCodeExport class writes the actual GCode. This is the only class that knows how GCode looks and feels.
|
||||
// Any customizations on GCodes flavors are done in this class.
|
||||
class GCodeExport
|
||||
{
|
||||
private:
|
||||
FILE* f;
|
||||
double extrusionAmount;
|
||||
double extrusionPerMM;
|
||||
double retractionAmount;
|
||||
double retractionAmountPrime;
|
||||
int retractionZHop;
|
||||
std::ostream* output_stream;
|
||||
double extrusion_amount; // in mm or mm^3
|
||||
double extruderSwitchRetraction;
|
||||
double minimalExtrusionBeforeRetraction;
|
||||
double extrusionAmountAtPreviousRetraction;
|
||||
int extruderSwitchRetractionSpeed;
|
||||
int extruderSwitchPrimeSpeed;
|
||||
double retraction_extrusion_window;
|
||||
int retraction_count_max;
|
||||
std::deque<double> extrusion_amount_at_previous_n_retractions; // in mm or mm^3
|
||||
Point3 currentPosition;
|
||||
Point3 startPosition;
|
||||
Point extruderOffset[MAX_EXTRUDERS];
|
||||
char extruderCharacter[MAX_EXTRUDERS];
|
||||
int currentSpeed, retractionSpeed;
|
||||
int currentTemperature[MAX_EXTRUDERS];
|
||||
int currentSpeed;
|
||||
int zPos;
|
||||
bool isRetracted;
|
||||
int extruderNr;
|
||||
bool isZHopped;
|
||||
int retractionPrimeSpeed;
|
||||
int current_extruder;
|
||||
int currentFanSpeed;
|
||||
int flavor;
|
||||
std::string preSwitchExtruderCode;
|
||||
std::string postSwitchExtruderCode;
|
||||
EGCodeFlavor flavor;
|
||||
std::string preSwitchExtruderCode[MAX_EXTRUDERS];
|
||||
std::string postSwitchExtruderCode[MAX_EXTRUDERS];
|
||||
|
||||
double totalFilament[MAX_EXTRUDERS];
|
||||
double totalFilament[MAX_EXTRUDERS]; // in mm^3
|
||||
double filament_diameter[MAX_EXTRUDERS]; // in mm^3
|
||||
double totalPrintTime;
|
||||
TimeEstimateCalculator estimateCalculator;
|
||||
|
||||
bool is_volumatric;
|
||||
public:
|
||||
|
||||
GCodeExport();
|
||||
|
||||
~GCodeExport();
|
||||
|
||||
void replaceTagInStart(const char* tag, const char* replaceValue);
|
||||
void setOutputStream(std::ostream* stream);
|
||||
|
||||
void setExtruderOffset(int id, Point p);
|
||||
void setSwitchExtruderCode(std::string preSwitchExtruderCode, std::string postSwitchExtruderCode);
|
||||
Point getExtruderOffset(int id);
|
||||
void setSwitchExtruderCode(int id, std::string preSwitchExtruderCode, std::string postSwitchExtruderCode);
|
||||
|
||||
void setFlavor(int flavor);
|
||||
int getFlavor();
|
||||
|
||||
void setFilename(const char* filename);
|
||||
|
||||
bool isOpened();
|
||||
|
||||
void setExtrusion(int layerThickness, int filamentDiameter, int flow);
|
||||
|
||||
void setRetractionSettings(int retractionAmount, int retractionSpeed, int extruderSwitchRetraction, int minimalExtrusionBeforeRetraction, int zHop, int retractionAmountPrime);
|
||||
void setFlavor(EGCodeFlavor flavor);
|
||||
EGCodeFlavor getFlavor();
|
||||
|
||||
void setRetractionSettings(int extruderSwitchRetraction, int extruderSwitchRetractionSpeed, int extruderSwitchPrimeSpeed, int minimalExtrusionBeforeRetraction, int retraction_count_max);
|
||||
|
||||
void setZ(int z);
|
||||
|
||||
Point3 getPosition();
|
||||
|
||||
Point getPositionXY();
|
||||
|
||||
void resetStartPosition();
|
||||
|
||||
Point getStartPositionXY();
|
||||
|
||||
int getPositionZ();
|
||||
|
||||
int getExtruderNr();
|
||||
|
||||
void setFilamentDiameter(unsigned int n, int diameter);
|
||||
double getFilamentArea(unsigned int extruder);
|
||||
|
||||
double getExtrusionAmountMM3(unsigned int extruder);
|
||||
|
||||
double getTotalFilamentUsed(int e);
|
||||
|
||||
double getTotalPrintTime();
|
||||
void updateTotalPrintTime();
|
||||
void resetTotalPrintTimeAndFilament();
|
||||
|
||||
void writeComment(const char* comment, ...);
|
||||
|
||||
void writeLine(const char* line, ...);
|
||||
void writeComment(std::string comment);
|
||||
void writeTypeComment(const char* type);
|
||||
void writeLayerComment(int layer_nr);
|
||||
|
||||
void writeLine(const char* line);
|
||||
|
||||
void resetExtrusionValue();
|
||||
|
||||
void writeDelay(double timeAmount);
|
||||
|
||||
void writeMove(Point p, int speed, int lineWidth);
|
||||
void writeMove(Point p, int speed, double extrusion_per_mm);
|
||||
|
||||
void writeRetraction(bool force=false);
|
||||
void writeMove(Point3 p, int speed, double extrusion_per_mm);
|
||||
private:
|
||||
void writeMove(int x, int y, int z, int speed, double extrusion_per_mm);
|
||||
public:
|
||||
void writeRetraction(RetractionConfig* config, bool force=false);
|
||||
|
||||
void switchExtruder(int newExtruder);
|
||||
|
||||
@@ -94,137 +188,12 @@ public:
|
||||
|
||||
void writeFanCommand(int speed);
|
||||
|
||||
void writeTemperatureCommand(int extruder, int temperature, bool wait = false);
|
||||
void writeBedTemperatureCommand(int temperature, bool wait = false);
|
||||
|
||||
void finalize(int maxObjectHeight, int moveSpeed, const char* endCode);
|
||||
|
||||
int getFileSize();
|
||||
void tellFileSize();
|
||||
};
|
||||
|
||||
//The GCodePathConfig is the configuration for moves/extrusion actions. This defines at which width the line is printed and at which speed.
|
||||
class GCodePathConfig
|
||||
{
|
||||
public:
|
||||
int speed;
|
||||
int lineWidth;
|
||||
const char* name;
|
||||
bool spiralize;
|
||||
|
||||
GCodePathConfig() : speed(0), lineWidth(0), name(nullptr), spiralize(false) {}
|
||||
GCodePathConfig(int speed, int lineWidth, const char* name) : speed(speed), lineWidth(lineWidth), name(name), spiralize(false) {}
|
||||
|
||||
void setData(int speed, int lineWidth, const char* name)
|
||||
{
|
||||
this->speed = speed;
|
||||
this->lineWidth = lineWidth;
|
||||
this->name = name;
|
||||
}
|
||||
};
|
||||
|
||||
class GCodePath
|
||||
{
|
||||
public:
|
||||
GCodePathConfig* config;
|
||||
bool retract;
|
||||
int extruder;
|
||||
vector<Point> points;
|
||||
bool done;//Path is finished, no more moves should be added, and a new path should be started instead of any appending done to this one.
|
||||
};
|
||||
|
||||
//The GCodePlanner class stores multiple moves that are planned.
|
||||
// It facilitates the combing to keep the head inside the print.
|
||||
// It also keeps track of the print time estimate for this planning so speed adjustments can be made for the minimal-layer-time.
|
||||
class GCodePlanner
|
||||
{
|
||||
private:
|
||||
GCodeExport& gcode;
|
||||
|
||||
Point lastPosition;
|
||||
vector<GCodePath> paths;
|
||||
Comb* comb;
|
||||
|
||||
GCodePathConfig travelConfig;
|
||||
int extrudeSpeedFactor;
|
||||
int travelSpeedFactor;
|
||||
int currentExtruder;
|
||||
int retractionMinimalDistance;
|
||||
bool forceRetraction;
|
||||
bool alwaysRetract;
|
||||
double extraTime;
|
||||
double totalPrintTime;
|
||||
private:
|
||||
GCodePath* getLatestPathWithConfig(GCodePathConfig* config);
|
||||
void forceNewPathStart();
|
||||
public:
|
||||
GCodePlanner(GCodeExport& gcode, int travelSpeed, int retractionMinimalDistance);
|
||||
~GCodePlanner();
|
||||
|
||||
bool setExtruder(int extruder)
|
||||
{
|
||||
if (extruder == currentExtruder)
|
||||
return false;
|
||||
currentExtruder = extruder;
|
||||
return true;
|
||||
}
|
||||
|
||||
int getExtruder()
|
||||
{
|
||||
return currentExtruder;
|
||||
}
|
||||
|
||||
void setCombBoundary(Polygons* polygons)
|
||||
{
|
||||
if (comb)
|
||||
delete comb;
|
||||
if (polygons)
|
||||
comb = new Comb(*polygons);
|
||||
else
|
||||
comb = nullptr;
|
||||
}
|
||||
|
||||
void setAlwaysRetract(bool alwaysRetract)
|
||||
{
|
||||
this->alwaysRetract = alwaysRetract;
|
||||
}
|
||||
|
||||
void forceRetract()
|
||||
{
|
||||
forceRetraction = true;
|
||||
}
|
||||
|
||||
void setExtrudeSpeedFactor(int speedFactor)
|
||||
{
|
||||
if (speedFactor < 1) speedFactor = 1;
|
||||
this->extrudeSpeedFactor = speedFactor;
|
||||
}
|
||||
int getExtrudeSpeedFactor()
|
||||
{
|
||||
return this->extrudeSpeedFactor;
|
||||
}
|
||||
void setTravelSpeedFactor(int speedFactor)
|
||||
{
|
||||
if (speedFactor < 1) speedFactor = 1;
|
||||
this->travelSpeedFactor = speedFactor;
|
||||
}
|
||||
int getTravelSpeedFactor()
|
||||
{
|
||||
return this->travelSpeedFactor;
|
||||
}
|
||||
|
||||
void addTravel(Point p);
|
||||
|
||||
void addExtrusionMove(Point p, GCodePathConfig* config);
|
||||
|
||||
void moveInsideCombBoundary(int distance);
|
||||
|
||||
void addPolygon(PolygonRef polygon, int startIdx, GCodePathConfig* config);
|
||||
|
||||
void addPolygonsByOptimizer(Polygons& polygons, GCodePathConfig* config);
|
||||
|
||||
void forceMinimalLayerTime(double minTime, int minimalSpeed);
|
||||
|
||||
void writeGCode(bool liftHeadIfNeeded, int layerThickness);
|
||||
};
|
||||
|
||||
}//namespace cura
|
||||
}
|
||||
|
||||
#endif//GCODEEXPORT_H
|
||||
|
||||
@@ -0,0 +1,313 @@
|
||||
#include "gcodePlanner.h"
|
||||
#include "pathOrderOptimizer.h"
|
||||
|
||||
namespace cura {
|
||||
|
||||
GCodePath* GCodePlanner::getLatestPathWithConfig(GCodePathConfig* config)
|
||||
{
|
||||
if (paths.size() > 0 && paths[paths.size()-1].config == config && !paths[paths.size()-1].done)
|
||||
return &paths[paths.size()-1];
|
||||
paths.push_back(GCodePath());
|
||||
GCodePath* ret = &paths[paths.size()-1];
|
||||
ret->retract = false;
|
||||
ret->config = config;
|
||||
ret->extruder = currentExtruder;
|
||||
ret->done = false;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void GCodePlanner::forceNewPathStart()
|
||||
{
|
||||
if (paths.size() > 0)
|
||||
paths[paths.size()-1].done = true;
|
||||
}
|
||||
|
||||
GCodePlanner::GCodePlanner(GCodeExport& gcode, RetractionConfig* retraction_config, int travelSpeed, int retractionMinimalDistance)
|
||||
: gcode(gcode), travelConfig(retraction_config, "MOVE")
|
||||
{
|
||||
lastPosition = gcode.getPositionXY();
|
||||
travelConfig.setSpeed(travelSpeed);
|
||||
comb = nullptr;
|
||||
extrudeSpeedFactor = 100;
|
||||
travelSpeedFactor = 100;
|
||||
extraTime = 0.0;
|
||||
totalPrintTime = 0.0;
|
||||
forceRetraction = false;
|
||||
alwaysRetract = false;
|
||||
currentExtruder = gcode.getExtruderNr();
|
||||
this->retractionMinimalDistance = retractionMinimalDistance;
|
||||
}
|
||||
GCodePlanner::~GCodePlanner()
|
||||
{
|
||||
if (comb)
|
||||
delete comb;
|
||||
}
|
||||
|
||||
void GCodePlanner::addTravel(Point p)
|
||||
{
|
||||
GCodePath* path = getLatestPathWithConfig(&travelConfig);
|
||||
if (forceRetraction)
|
||||
{
|
||||
if (!shorterThen(lastPosition - p, retractionMinimalDistance))
|
||||
{
|
||||
path->retract = true;
|
||||
}
|
||||
forceRetraction = false;
|
||||
}else if (comb != nullptr)
|
||||
{
|
||||
std::vector<Point> pointList;
|
||||
if (comb->calc(lastPosition, p, pointList))
|
||||
{
|
||||
for(unsigned int n=0; n<pointList.size(); n++)
|
||||
{
|
||||
path->points.push_back(pointList[n]);
|
||||
}
|
||||
}else{
|
||||
if (!shorterThen(lastPosition - p, retractionMinimalDistance))
|
||||
path->retract = true;
|
||||
}
|
||||
}else if (alwaysRetract)
|
||||
{
|
||||
if (!shorterThen(lastPosition - p, retractionMinimalDistance))
|
||||
path->retract = true;
|
||||
}
|
||||
path->points.push_back(p);
|
||||
lastPosition = p;
|
||||
}
|
||||
|
||||
void GCodePlanner::addExtrusionMove(Point p, GCodePathConfig* config)
|
||||
{
|
||||
getLatestPathWithConfig(config)->points.push_back(p);
|
||||
lastPosition = p;
|
||||
}
|
||||
|
||||
void GCodePlanner::moveInsideCombBoundary(int distance)
|
||||
{
|
||||
if (!comb || comb->inside(lastPosition)) return;
|
||||
Point p = lastPosition;
|
||||
if (comb->moveInside(&p, distance))
|
||||
{
|
||||
//Move inside again, so we move out of tight 90deg corners
|
||||
comb->moveInside(&p, distance);
|
||||
if (comb->inside(p))
|
||||
{
|
||||
addTravel(p);
|
||||
//Make sure the that any retraction happens after this move, not before it by starting a new move path.
|
||||
forceNewPathStart();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GCodePlanner::addPolygon(PolygonRef polygon, int startIdx, GCodePathConfig* config)
|
||||
{
|
||||
Point p0 = polygon[startIdx];
|
||||
addTravel(p0);
|
||||
for(unsigned int i=1; i<polygon.size(); i++)
|
||||
{
|
||||
Point p1 = polygon[(startIdx + i) % polygon.size()];
|
||||
addExtrusionMove(p1, config);
|
||||
p0 = p1;
|
||||
}
|
||||
if (polygon.size() > 2)
|
||||
addExtrusionMove(polygon[startIdx], config);
|
||||
}
|
||||
|
||||
void GCodePlanner::addPolygonsByOptimizer(Polygons& polygons, GCodePathConfig* config)
|
||||
{
|
||||
//log("addPolygonsByOptimizer");
|
||||
PathOrderOptimizer orderOptimizer(lastPosition);
|
||||
for(unsigned int i=0;i<polygons.size();i++)
|
||||
orderOptimizer.addPolygon(polygons[i]);
|
||||
orderOptimizer.optimize();
|
||||
for(unsigned int i=0;i<orderOptimizer.polyOrder.size();i++)
|
||||
{
|
||||
int nr = orderOptimizer.polyOrder[i];
|
||||
addPolygon(polygons[nr], orderOptimizer.polyStart[nr], config);
|
||||
}
|
||||
}
|
||||
void GCodePlanner::addLinesByOptimizer(Polygons& polygons, GCodePathConfig* config)
|
||||
{
|
||||
LineOrderOptimizer orderOptimizer(lastPosition);
|
||||
for(unsigned int i=0;i<polygons.size();i++)
|
||||
orderOptimizer.addPolygon(polygons[i]);
|
||||
orderOptimizer.optimize();
|
||||
for(unsigned int i=0;i<orderOptimizer.polyOrder.size();i++)
|
||||
{
|
||||
int nr = orderOptimizer.polyOrder[i];
|
||||
addPolygon(polygons[nr], orderOptimizer.polyStart[nr], config);
|
||||
}
|
||||
}
|
||||
|
||||
void GCodePlanner::forceMinimalLayerTime(double minTime, int minimalSpeed, double travelTime, double extrudeTime)
|
||||
{
|
||||
double totalTime = travelTime + extrudeTime;
|
||||
if (totalTime < minTime && extrudeTime > 0.0)
|
||||
{
|
||||
double minExtrudeTime = minTime - travelTime;
|
||||
if (minExtrudeTime < 1)
|
||||
minExtrudeTime = 1;
|
||||
double factor = extrudeTime / minExtrudeTime;
|
||||
for(unsigned int n=0; n<paths.size(); n++)
|
||||
{
|
||||
GCodePath* path = &paths[n];
|
||||
if (path->config->getExtrusionMM3perMM() == 0)
|
||||
continue;
|
||||
int speed = path->config->getSpeed() * factor;
|
||||
if (speed < minimalSpeed)
|
||||
factor = double(minimalSpeed) / double(path->config->getSpeed());
|
||||
}
|
||||
|
||||
//Only slow down with the minimal time if that will be slower then a factor already set. First layer slowdown also sets the speed factor.
|
||||
if (factor * 100 < getExtrudeSpeedFactor())
|
||||
setExtrudeSpeedFactor(factor * 100);
|
||||
else
|
||||
factor = getExtrudeSpeedFactor() / 100.0;
|
||||
|
||||
if (minTime - (extrudeTime / factor) - travelTime > 0.1)
|
||||
{
|
||||
this->extraTime = minTime - (extrudeTime / factor) - travelTime;
|
||||
}
|
||||
this->totalPrintTime = (extrudeTime / factor) + travelTime;
|
||||
}else{
|
||||
this->totalPrintTime = totalTime;
|
||||
}
|
||||
}
|
||||
|
||||
void GCodePlanner::getTimes(double& travelTime, double& extrudeTime)
|
||||
{
|
||||
travelTime = 0.0;
|
||||
extrudeTime = 0.0;
|
||||
Point p0 = gcode.getPositionXY();
|
||||
for(unsigned int n=0; n<paths.size(); n++)
|
||||
{
|
||||
GCodePath* path = &paths[n];
|
||||
for(unsigned int i=0; i<path->points.size(); i++)
|
||||
{
|
||||
double thisTime = vSizeMM(p0 - path->points[i]) / double(path->config->getSpeed());
|
||||
if (path->config->getExtrusionMM3perMM() != 0)
|
||||
extrudeTime += thisTime;
|
||||
else
|
||||
travelTime += thisTime;
|
||||
p0 = path->points[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GCodePlanner::writeGCode(bool liftHeadIfNeeded, int layerThickness)
|
||||
{
|
||||
GCodePathConfig* lastConfig = nullptr;
|
||||
int extruder = gcode.getExtruderNr();
|
||||
|
||||
for(unsigned int n=0; n<paths.size(); n++)
|
||||
{
|
||||
GCodePath* path = &paths[n];
|
||||
if (extruder != path->extruder)
|
||||
{
|
||||
extruder = path->extruder;
|
||||
gcode.switchExtruder(extruder);
|
||||
}else if (path->retract)
|
||||
{
|
||||
gcode.writeRetraction(path->config->retraction_config);
|
||||
}
|
||||
if (path->config != &travelConfig && lastConfig != path->config)
|
||||
{
|
||||
gcode.writeTypeComment(path->config->name);
|
||||
lastConfig = path->config;
|
||||
}
|
||||
int speed = path->config->getSpeed();
|
||||
|
||||
if (path->config->getExtrusionMM3perMM() != 0)// Only apply the extrudeSpeedFactor to extrusion moves
|
||||
speed = speed * extrudeSpeedFactor / 100;
|
||||
else
|
||||
speed = speed * travelSpeedFactor / 100;
|
||||
|
||||
if (path->points.size() == 1 && path->config != &travelConfig && shorterThen(gcode.getPositionXY() - path->points[0], path->config->getLineWidth() * 2))
|
||||
{
|
||||
//Check for lots of small moves and combine them into one large line
|
||||
Point p0 = path->points[0];
|
||||
unsigned int i = n + 1;
|
||||
while(i < paths.size() && paths[i].points.size() == 1 && shorterThen(p0 - paths[i].points[0], path->config->getLineWidth() * 2))
|
||||
{
|
||||
p0 = paths[i].points[0];
|
||||
i ++;
|
||||
}
|
||||
if (paths[i-1].config == &travelConfig)
|
||||
i --;
|
||||
if (i > n + 2)
|
||||
{
|
||||
p0 = gcode.getPositionXY();
|
||||
for(unsigned int x=n; x<i-1; x+=2)
|
||||
{
|
||||
int64_t new_width = vSize(p0 - paths[x].points[0]); // = old_length
|
||||
Point newPoint = (paths[x].points[0] + paths[x+1].points[0]) / 2;
|
||||
int64_t old_width = path->config->getLineWidth();
|
||||
if (old_width > 0)
|
||||
{
|
||||
if (new_width > 0)
|
||||
gcode.writeMove(newPoint, speed * old_width / new_width, path->config->getExtrusionMM3perMM() * new_width / old_width);
|
||||
else
|
||||
gcode.writeMove(newPoint, speed, path->config->getExtrusionMM3perMM());
|
||||
}
|
||||
p0 = paths[x+1].points[0];
|
||||
}
|
||||
gcode.writeMove(paths[i-1].points[0], speed, path->config->getExtrusionMM3perMM());
|
||||
n = i - 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
bool spiralize = path->config->spiralize;
|
||||
if (spiralize)
|
||||
{
|
||||
//Check if we are the last spiralize path in the list, if not, do not spiralize.
|
||||
for(unsigned int m=n+1; m<paths.size(); m++)
|
||||
{
|
||||
if (paths[m].config->spiralize)
|
||||
spiralize = false;
|
||||
}
|
||||
}
|
||||
if (spiralize)
|
||||
{
|
||||
//If we need to spiralize then raise the head slowly by 1 layer as this path progresses.
|
||||
float totalLength = 0.0;
|
||||
int z = gcode.getPositionZ();
|
||||
Point p0 = gcode.getPositionXY();
|
||||
for(unsigned int i=0; i<path->points.size(); i++)
|
||||
{
|
||||
Point p1 = path->points[i];
|
||||
totalLength += vSizeMM(p0 - p1);
|
||||
p0 = p1;
|
||||
}
|
||||
|
||||
float length = 0.0;
|
||||
p0 = gcode.getPositionXY();
|
||||
for(unsigned int i=0; i<path->points.size(); i++)
|
||||
{
|
||||
Point p1 = path->points[i];
|
||||
length += vSizeMM(p0 - p1);
|
||||
p0 = p1;
|
||||
gcode.setZ(z + layerThickness * length / totalLength);
|
||||
gcode.writeMove(path->points[i], speed, path->config->getExtrusionMM3perMM());
|
||||
}
|
||||
}else{
|
||||
for(unsigned int i=0; i<path->points.size(); i++)
|
||||
{
|
||||
gcode.writeMove(path->points[i], speed, path->config->getExtrusionMM3perMM());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gcode.updateTotalPrintTime();
|
||||
if (liftHeadIfNeeded && extraTime > 0.0)
|
||||
{
|
||||
gcode.writeComment("Small layer, adding delay");
|
||||
if (lastConfig)
|
||||
gcode.writeRetraction(lastConfig->retraction_config, true);
|
||||
gcode.setZ(gcode.getPositionZ() + MM2INT(3.0));
|
||||
gcode.writeMove(gcode.getPositionXY(), travelConfig.getSpeed(), 0);
|
||||
gcode.writeMove(gcode.getPositionXY() - Point(-MM2INT(20.0), 0), travelConfig.getSpeed(), 0);
|
||||
gcode.writeDelay(extraTime);
|
||||
}
|
||||
}
|
||||
|
||||
}//namespace cura
|
||||
@@ -0,0 +1,126 @@
|
||||
#ifndef GCODE_PLANNER_H
|
||||
#define GCODE_PLANNER_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "gcodeExport.h"
|
||||
#include "comb.h"
|
||||
#include "utils/polygon.h"
|
||||
#include "utils/logoutput.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
class GCodePath
|
||||
{
|
||||
public:
|
||||
GCodePathConfig* config;
|
||||
bool retract;
|
||||
int extruder;
|
||||
std::vector<Point> points;
|
||||
bool done;//Path is finished, no more moves should be added, and a new path should be started instead of any appending done to this one.
|
||||
};
|
||||
|
||||
//The GCodePlanner class stores multiple moves that are planned.
|
||||
// It facilitates the combing to keep the head inside the print.
|
||||
// It also keeps track of the print time estimate for this planning so speed adjustments can be made for the minimal-layer-time.
|
||||
class GCodePlanner
|
||||
{
|
||||
private:
|
||||
GCodeExport& gcode;
|
||||
|
||||
Point lastPosition;
|
||||
std::vector<GCodePath> paths;
|
||||
Comb* comb;
|
||||
|
||||
GCodePathConfig travelConfig;
|
||||
int extrudeSpeedFactor;
|
||||
int travelSpeedFactor;
|
||||
int currentExtruder;
|
||||
int retractionMinimalDistance;
|
||||
bool forceRetraction;
|
||||
bool alwaysRetract;
|
||||
double extraTime;
|
||||
double totalPrintTime;
|
||||
|
||||
private:
|
||||
GCodePath* getLatestPathWithConfig(GCodePathConfig* config);
|
||||
void forceNewPathStart();
|
||||
public:
|
||||
GCodePlanner(GCodeExport& gcode, RetractionConfig* retraction_config, int travelSpeed, int retractionMinimalDistance);
|
||||
~GCodePlanner();
|
||||
|
||||
bool setExtruder(int extruder)
|
||||
{
|
||||
if (extruder == currentExtruder)
|
||||
return false;
|
||||
currentExtruder = extruder;
|
||||
return true;
|
||||
}
|
||||
|
||||
int getExtruder()
|
||||
{
|
||||
return currentExtruder;
|
||||
}
|
||||
|
||||
void setCombBoundary(Polygons* polygons)
|
||||
{
|
||||
if (comb)
|
||||
delete comb;
|
||||
if (polygons)
|
||||
comb = new Comb(*polygons);
|
||||
else
|
||||
comb = nullptr;
|
||||
}
|
||||
|
||||
void setAlwaysRetract(bool alwaysRetract)
|
||||
{
|
||||
this->alwaysRetract = alwaysRetract;
|
||||
}
|
||||
|
||||
void forceRetract()
|
||||
{
|
||||
forceRetraction = true;
|
||||
}
|
||||
|
||||
void setExtrudeSpeedFactor(int speedFactor)
|
||||
{
|
||||
if (speedFactor < 1) speedFactor = 1;
|
||||
this->extrudeSpeedFactor = speedFactor;
|
||||
}
|
||||
int getExtrudeSpeedFactor()
|
||||
{
|
||||
return this->extrudeSpeedFactor;
|
||||
}
|
||||
void setTravelSpeedFactor(int speedFactor)
|
||||
{
|
||||
if (speedFactor < 1) speedFactor = 1;
|
||||
this->travelSpeedFactor = speedFactor;
|
||||
}
|
||||
int getTravelSpeedFactor()
|
||||
{
|
||||
return this->travelSpeedFactor;
|
||||
}
|
||||
|
||||
void addTravel(Point p);
|
||||
|
||||
void addExtrusionMove(Point p, GCodePathConfig* config);
|
||||
|
||||
void moveInsideCombBoundary(int distance);
|
||||
|
||||
void addPolygon(PolygonRef polygon, int startIdx, GCodePathConfig* config);
|
||||
|
||||
void addPolygonsByOptimizer(Polygons& polygons, GCodePathConfig* config);
|
||||
|
||||
void addLinesByOptimizer(Polygons& polygons, GCodePathConfig* config);
|
||||
|
||||
void forceMinimalLayerTime(double minTime, int minimalSpeed, double travelTime, double extrusionTime);
|
||||
|
||||
void getTimes(double& travelTime, double& extrudeTime);
|
||||
|
||||
void writeGCode(bool liftHeadIfNeeded, int layerThickness);
|
||||
};
|
||||
|
||||
}//namespace cura
|
||||
|
||||
#endif//GCODE_PLANNER_H
|
||||
+430
-61
@@ -1,8 +1,27 @@
|
||||
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
|
||||
#include "infill.h"
|
||||
|
||||
#include "functional"
|
||||
#include "utils/polygonUtils.h"
|
||||
namespace cura {
|
||||
|
||||
|
||||
|
||||
void generateConcentricInfillDense(Polygons outline, Polygons& result, Polygons* in_between, int extrusionWidth, bool avoidOverlappingPerimeters)
|
||||
{
|
||||
while(outline.size() > 0)
|
||||
{
|
||||
for (unsigned int polyNr = 0; polyNr < outline.size(); polyNr++)
|
||||
{
|
||||
PolygonRef r = outline[polyNr];
|
||||
result.add(r);
|
||||
}
|
||||
Polygons next_outline;
|
||||
offsetExtrusionWidth(outline, true, extrusionWidth, next_outline, in_between, avoidOverlappingPerimeters);
|
||||
outline = next_outline;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void generateConcentricInfill(Polygons outline, Polygons& result, int inset_value)
|
||||
{
|
||||
while(outline.size() > 0)
|
||||
@@ -13,95 +32,445 @@ void generateConcentricInfill(Polygons outline, Polygons& result, int inset_valu
|
||||
result.add(r);
|
||||
}
|
||||
outline = outline.offset(-inset_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void generateAutomaticInfill(const Polygons& in_outline, Polygons& result,
|
||||
int extrusionWidth, int lineSpacing,
|
||||
int infillOverlap, double rotation)
|
||||
{
|
||||
if (lineSpacing > extrusionWidth * 4)
|
||||
{
|
||||
generateGridInfill(in_outline, result, extrusionWidth, lineSpacing,
|
||||
infillOverlap, rotation);
|
||||
}
|
||||
else
|
||||
{
|
||||
generateLineInfill(in_outline, result, extrusionWidth, lineSpacing,
|
||||
infillOverlap, rotation);
|
||||
}
|
||||
}
|
||||
|
||||
void generateGridInfill(const Polygons& in_outline, Polygons& result,
|
||||
void generateGridInfill(const Polygons& in_outline, int outlineOffset, Polygons& result,
|
||||
int extrusionWidth, int lineSpacing, int infillOverlap,
|
||||
double rotation)
|
||||
{
|
||||
generateLineInfill(in_outline, result, extrusionWidth, lineSpacing * 2,
|
||||
generateLineInfill(in_outline, outlineOffset, result, extrusionWidth, lineSpacing,
|
||||
infillOverlap, rotation);
|
||||
generateLineInfill(in_outline, result, extrusionWidth, lineSpacing * 2,
|
||||
generateLineInfill(in_outline, outlineOffset, result, extrusionWidth, lineSpacing,
|
||||
infillOverlap, rotation + 90);
|
||||
}
|
||||
|
||||
int compare_int64_t(const void* a, const void* b)
|
||||
void generateTriangleInfill(const Polygons& in_outline, int outlineOffset, Polygons& result,
|
||||
int extrusionWidth, int lineSpacing, int infillOverlap,
|
||||
double rotation)
|
||||
{
|
||||
int64_t n = (*(int64_t*)a) - (*(int64_t*)b);
|
||||
if (n < 0) return -1;
|
||||
if (n > 0) return 1;
|
||||
return 0;
|
||||
generateLineInfill(in_outline, outlineOffset, result, extrusionWidth, lineSpacing,
|
||||
infillOverlap, rotation);
|
||||
generateLineInfill(in_outline, outlineOffset, result, extrusionWidth, lineSpacing,
|
||||
infillOverlap, rotation + 60);
|
||||
generateLineInfill(in_outline, outlineOffset, result, extrusionWidth, lineSpacing,
|
||||
infillOverlap, rotation + 120);
|
||||
}
|
||||
|
||||
void generateLineInfill(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, int infillOverlap, double rotation)
|
||||
void addLineInfill(Polygons& result, PointMatrix matrix, int scanline_min_idx, int lineSpacing, AABB boundary, std::vector<std::vector<int64_t> > cutList, int extrusionWidth)
|
||||
{
|
||||
Polygons outline = in_outline.offset(extrusionWidth * infillOverlap / 100);
|
||||
auto addLine = [&](Point from, Point to)
|
||||
{
|
||||
PolygonRef p = result.newPoly();
|
||||
p.add(matrix.unapply(from));
|
||||
p.add(matrix.unapply(to));
|
||||
};
|
||||
|
||||
auto compare_int64_t = [](const void* a, const void* b)
|
||||
{
|
||||
int64_t n = (*(int64_t*)a) - (*(int64_t*)b);
|
||||
if (n < 0) return -1;
|
||||
if (n > 0) return 1;
|
||||
return 0;
|
||||
};
|
||||
|
||||
int scanline_idx = 0;
|
||||
for(int64_t x = scanline_min_idx * lineSpacing; x < boundary.max.X; x += lineSpacing)
|
||||
{
|
||||
qsort(cutList[scanline_idx].data(), cutList[scanline_idx].size(), sizeof(int64_t), compare_int64_t);
|
||||
for(unsigned int i = 0; i + 1 < cutList[scanline_idx].size(); i+=2)
|
||||
{
|
||||
if (cutList[scanline_idx][i+1] - cutList[scanline_idx][i] < extrusionWidth / 5)
|
||||
continue;
|
||||
addLine(Point(x, cutList[scanline_idx][i]), Point(x, cutList[scanline_idx][i+1]));
|
||||
}
|
||||
scanline_idx += 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* generate lines within the area of \p in_outline, at regular intervals of \p lineSpacing
|
||||
*
|
||||
* idea:
|
||||
* intersect a regular grid of 'scanlines' with the area inside \p in_outline
|
||||
*
|
||||
* we call the areas between two consecutive scanlines a 'scansegment'.
|
||||
* Scansegment x is the area between scanline x and scanline x+1
|
||||
*
|
||||
* algorithm:
|
||||
* 1) for each line segment of each polygon:
|
||||
* store the intersections of that line segment with all scanlines in a mapping (vector of vectors) from scanline to intersections
|
||||
* (zigzag): add boundary segments to result
|
||||
* 2) for each scanline:
|
||||
* sort the associated intersections
|
||||
* and connect them using the even-odd rule
|
||||
*
|
||||
*/
|
||||
void generateLineInfill(const Polygons& in_outline, int outlineOffset, Polygons& result, int extrusionWidth, int lineSpacing, int infillOverlap, double rotation)
|
||||
{
|
||||
if (in_outline.size() == 0) return;
|
||||
Polygons outline = in_outline.offset(extrusionWidth * infillOverlap / 100 + outlineOffset);
|
||||
if (outline.size() == 0) return;
|
||||
|
||||
PointMatrix matrix(rotation);
|
||||
|
||||
outline.applyMatrix(matrix);
|
||||
|
||||
|
||||
AABB boundary(outline);
|
||||
|
||||
int scanline_min_idx = boundary.min.X / lineSpacing;
|
||||
int lineCount = (boundary.max.X + (lineSpacing - 1)) / lineSpacing - scanline_min_idx;
|
||||
|
||||
std::vector<std::vector<int64_t> > cutList; // mapping from scanline to all intersections with polygon segments
|
||||
|
||||
for(int n=0; n<lineCount; n++)
|
||||
cutList.push_back(std::vector<int64_t>());
|
||||
|
||||
for(unsigned int poly_idx=0; poly_idx < outline.size(); poly_idx++)
|
||||
{
|
||||
Point p0 = outline[poly_idx][outline[poly_idx].size()-1];
|
||||
for(unsigned int i=0; i < outline[poly_idx].size(); i++)
|
||||
{
|
||||
Point p1 = outline[poly_idx][i];
|
||||
int64_t xMin = p1.X, xMax = p0.X;
|
||||
if (xMin == xMax) {
|
||||
p0 = p1;
|
||||
continue;
|
||||
}
|
||||
if (xMin > xMax) { xMin = p0.X; xMax = p1.X; }
|
||||
|
||||
int scanline_idx0 = (p0.X + ((p0.X > 0)? -1 : -lineSpacing)) / lineSpacing; // -1 cause a linesegment on scanline x counts as belonging to scansegment x-1 ...
|
||||
int scanline_idx1 = (p1.X + ((p1.X > 0)? -1 : -lineSpacing)) / lineSpacing; // -linespacing because a line between scanline -n and -n-1 belongs to scansegment -n-1 (for n=positive natural number)
|
||||
int direction = 1;
|
||||
if (p0.X > p1.X)
|
||||
{
|
||||
direction = -1;
|
||||
scanline_idx1 += 1; // only consider the scanlines in between the scansegments
|
||||
} else scanline_idx0 += 1; // only consider the scanlines in between the scansegments
|
||||
|
||||
for(int scanline_idx = scanline_idx0; scanline_idx != scanline_idx1+direction; scanline_idx+=direction)
|
||||
{
|
||||
int x = scanline_idx * lineSpacing;
|
||||
int y = p1.Y + (p0.Y - p1.Y) * (x - p1.X) / (p0.X - p1.X);
|
||||
cutList[scanline_idx - scanline_min_idx].push_back(y);
|
||||
}
|
||||
p0 = p1;
|
||||
}
|
||||
}
|
||||
|
||||
addLineInfill(result, matrix, scanline_min_idx, lineSpacing, boundary, cutList, extrusionWidth);
|
||||
}
|
||||
|
||||
|
||||
void generateZigZagInfill(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, int infillOverlap, double rotation, bool connect_zigzags, bool use_endPieces)
|
||||
{
|
||||
if (use_endPieces) return generateZigZagInfill_endPieces(in_outline, result, extrusionWidth, lineSpacing, infillOverlap, rotation, connect_zigzags);
|
||||
else return generateZigZagInfill_noEndPieces(in_outline, result, extrusionWidth, lineSpacing, infillOverlap, rotation);
|
||||
}
|
||||
|
||||
/*!
|
||||
* adapted from generateLineInfill(.)
|
||||
*
|
||||
* generate lines within the area of [in_outline], at regular intervals of [lineSpacing]
|
||||
* idea:
|
||||
* intersect a regular grid of 'scanlines' with the area inside [in_outline]
|
||||
* sigzag:
|
||||
* include pieces of boundary, connecting the lines, forming an accordion like zigzag instead of separate lines |_|^|_|
|
||||
*
|
||||
* we call the areas between two consecutive scanlines a 'scansegment'
|
||||
*
|
||||
* algorithm:
|
||||
* 1. for each line segment of each polygon:
|
||||
* store the intersections of that line segment with all scanlines in a mapping (vector of vectors) from scanline to intersections
|
||||
* (zigzag): add boundary segments to result
|
||||
* 2. for each scanline:
|
||||
* sort the associated intersections
|
||||
* and connect them using the even-odd rule
|
||||
*
|
||||
* zigzag algorithm:
|
||||
* while walking around (each) polygon (1.)
|
||||
* if polygon intersects with even scanline
|
||||
* start boundary segment (add each following segment to the [result])
|
||||
* when polygon intersects with a scanline again
|
||||
* stop boundary segment (stop adding segments to the [result])
|
||||
* if polygon intersects with even scanline again (instead of odd)
|
||||
* dont add the last line segment to the boundary (unless [connect_zigzags])
|
||||
*
|
||||
*
|
||||
* <--
|
||||
* ___
|
||||
* | | |
|
||||
* | | |
|
||||
* | |___|
|
||||
* -->
|
||||
*
|
||||
* ^ = even scanline
|
||||
*
|
||||
* start boundary from even scanline! :D
|
||||
*
|
||||
*
|
||||
* _____
|
||||
* | | | ,
|
||||
* | | | |
|
||||
* |_____| |__/
|
||||
*
|
||||
* ^ ^ ^ scanlines
|
||||
* ^ disconnected end piece
|
||||
*/
|
||||
void generateZigZagInfill_endPieces(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, int infillOverlap, double rotation, bool connect_zigzags)
|
||||
{
|
||||
// if (in_outline.size() == 0) return;
|
||||
// Polygons outline = in_outline.offset(extrusionWidth * infillOverlap / 100 - extrusionWidth / 2);
|
||||
Polygons empty;
|
||||
Polygons outline = in_outline.difference(empty); // copy
|
||||
if (outline.size() == 0) return;
|
||||
|
||||
PointMatrix matrix(rotation);
|
||||
|
||||
outline.applyMatrix(matrix);
|
||||
|
||||
auto addLine = [&](Point from, Point to)
|
||||
{
|
||||
PolygonRef p = result.newPoly();
|
||||
p.add(matrix.unapply(from));
|
||||
p.add(matrix.unapply(to));
|
||||
};
|
||||
|
||||
AABB boundary(outline);
|
||||
|
||||
boundary.min.X = ((boundary.min.X / lineSpacing) - 1) * lineSpacing;
|
||||
int lineCount = (boundary.max.X - boundary.min.X + (lineSpacing - 1)) / lineSpacing;
|
||||
vector<vector<int64_t> > cutList;
|
||||
int scanline_min_idx = boundary.min.X / lineSpacing;
|
||||
int lineCount = (boundary.max.X + (lineSpacing - 1)) / lineSpacing - scanline_min_idx;
|
||||
|
||||
std::vector<std::vector<int64_t> > cutList; // mapping from scanline to all intersections with polygon segments
|
||||
|
||||
for(int n=0; n<lineCount; n++)
|
||||
cutList.push_back(vector<int64_t>());
|
||||
|
||||
cutList.push_back(std::vector<int64_t>());
|
||||
for(unsigned int polyNr=0; polyNr < outline.size(); polyNr++)
|
||||
{
|
||||
Point p1 = outline[polyNr][outline[polyNr].size()-1];
|
||||
std::vector<Point> firstBoundarySegment;
|
||||
std::vector<Point> unevenBoundarySegment; // stored cause for connected_zigzags a boundary segment which ends in an uneven scanline needs to be included
|
||||
|
||||
bool isFirstBoundarySegment = true;
|
||||
bool firstBoundarySegmentEndsInEven;
|
||||
|
||||
bool isEvenScanSegment = false;
|
||||
|
||||
|
||||
Point p0 = outline[polyNr][outline[polyNr].size()-1];
|
||||
Point lastPoint = p0;
|
||||
for(unsigned int i=0; i < outline[polyNr].size(); i++)
|
||||
{
|
||||
Point p0 = outline[polyNr][i];
|
||||
int idx0 = (p0.X - boundary.min.X) / lineSpacing;
|
||||
int idx1 = (p1.X - boundary.min.X) / lineSpacing;
|
||||
int64_t xMin = p0.X, xMax = p1.X;
|
||||
if (p0.X > p1.X) { xMin = p1.X; xMax = p0.X; }
|
||||
if (idx0 > idx1) { int tmp = idx0; idx0 = idx1; idx1 = tmp; }
|
||||
for(int idx = idx0; idx<=idx1; idx++)
|
||||
{
|
||||
int x = (idx * lineSpacing) + boundary.min.X + lineSpacing / 2;
|
||||
if (x < xMin) continue;
|
||||
if (x >= xMax) continue;
|
||||
int y = p0.Y + (p1.Y - p0.Y) * (x - p0.X) / (p1.X - p0.X);
|
||||
cutList[idx].push_back(y);
|
||||
Point p1 = outline[polyNr][i];
|
||||
int64_t xMin = p1.X, xMax = p0.X;
|
||||
if (xMin == xMax) {
|
||||
lastPoint = p1;
|
||||
p0 = p1;
|
||||
continue;
|
||||
}
|
||||
p1 = p0;
|
||||
if (xMin > xMax) { xMin = p0.X; xMax = p1.X; }
|
||||
|
||||
int scanline_idx0 = (p0.X + ((p0.X > 0)? -1 : -lineSpacing)) / lineSpacing; // -1 cause a linesegment on scanline x counts as belonging to scansegment x-1 ...
|
||||
int scanline_idx1 = (p1.X + ((p1.X > 0)? -1 : -lineSpacing)) / lineSpacing; // -linespacing because a line between scanline -n and -n-1 belongs to scansegment -n-1 (for n=positive natural number)
|
||||
int direction = 1;
|
||||
if (p0.X > p1.X)
|
||||
{
|
||||
direction = -1;
|
||||
scanline_idx1 += 1; // only consider the scanlines in between the scansegments
|
||||
} else scanline_idx0 += 1; // only consider the scanlines in between the scansegments
|
||||
|
||||
|
||||
if (isFirstBoundarySegment) firstBoundarySegment.push_back(p0);
|
||||
for(int scanline_idx = scanline_idx0; scanline_idx != scanline_idx1+direction; scanline_idx+=direction)
|
||||
{
|
||||
int x = scanline_idx * lineSpacing;
|
||||
int y = p1.Y + (p0.Y - p1.Y) * (x - p1.X) / (p0.X - p1.X);
|
||||
cutList[scanline_idx - scanline_min_idx].push_back(y);
|
||||
|
||||
|
||||
bool last_isEvenScanSegment = isEvenScanSegment;
|
||||
if (scanline_idx % 2 == 0) isEvenScanSegment = true;
|
||||
else isEvenScanSegment = false;
|
||||
|
||||
if (!isFirstBoundarySegment)
|
||||
{
|
||||
if (last_isEvenScanSegment && (connect_zigzags || !isEvenScanSegment))
|
||||
addLine(lastPoint, Point(x,y));
|
||||
else if (connect_zigzags && !last_isEvenScanSegment && !isEvenScanSegment) // if we end an uneven boundary in an uneven segment
|
||||
{ // add whole unevenBoundarySegment (including the just obtained point)
|
||||
for (unsigned int p = 1; p < unevenBoundarySegment.size(); p++)
|
||||
{
|
||||
addLine(unevenBoundarySegment[p-1], unevenBoundarySegment[p]);
|
||||
}
|
||||
addLine(unevenBoundarySegment[unevenBoundarySegment.size()-1], Point(x,y));
|
||||
unevenBoundarySegment.clear();
|
||||
}
|
||||
if (connect_zigzags && last_isEvenScanSegment && !isEvenScanSegment)
|
||||
unevenBoundarySegment.push_back(Point(x,y));
|
||||
else
|
||||
unevenBoundarySegment.clear();
|
||||
|
||||
}
|
||||
lastPoint = Point(x,y);
|
||||
|
||||
if (isFirstBoundarySegment)
|
||||
{
|
||||
firstBoundarySegment.emplace_back(x,y);
|
||||
firstBoundarySegmentEndsInEven = isEvenScanSegment;
|
||||
isFirstBoundarySegment = false;
|
||||
}
|
||||
|
||||
}
|
||||
if (!isFirstBoundarySegment)
|
||||
{
|
||||
if (isEvenScanSegment)
|
||||
addLine(lastPoint, p1);
|
||||
else if (connect_zigzags)
|
||||
unevenBoundarySegment.push_back(p1);
|
||||
}
|
||||
|
||||
lastPoint = p1;
|
||||
p0 = p1;
|
||||
}
|
||||
}
|
||||
|
||||
int idx = 0;
|
||||
for(int64_t x = boundary.min.X + lineSpacing / 2; x < boundary.max.X; x += lineSpacing)
|
||||
{
|
||||
qsort(cutList[idx].data(), cutList[idx].size(), sizeof(int64_t), compare_int64_t);
|
||||
for(unsigned int i = 0; i + 1 < cutList[idx].size(); i+=2)
|
||||
|
||||
if (isEvenScanSegment || isFirstBoundarySegment || connect_zigzags)
|
||||
{
|
||||
if (cutList[idx][i+1] - cutList[idx][i] < extrusionWidth / 5)
|
||||
continue;
|
||||
PolygonRef p = result.newPoly();
|
||||
p.add(matrix.unapply(Point(x, cutList[idx][i])));
|
||||
p.add(matrix.unapply(Point(x, cutList[idx][i+1])));
|
||||
for (unsigned int i = 1; i < firstBoundarySegment.size() ; i++)
|
||||
{
|
||||
if (i < firstBoundarySegment.size() - 1 || !firstBoundarySegmentEndsInEven || connect_zigzags) // only add last element if connect_zigzags or boundary segment ends in uneven scanline
|
||||
addLine(firstBoundarySegment[i-1], firstBoundarySegment[i]);
|
||||
}
|
||||
}
|
||||
idx += 1;
|
||||
}
|
||||
else if (!firstBoundarySegmentEndsInEven)
|
||||
addLine(firstBoundarySegment[firstBoundarySegment.size()-2], firstBoundarySegment[firstBoundarySegment.size()-1]);
|
||||
}
|
||||
|
||||
if (cutList.size() == 0) return;
|
||||
if (connect_zigzags && cutList.size() == 1 && cutList[0].size() <= 2) return; // don't add connection if boundary already contains whole outline!
|
||||
|
||||
addLineInfill(result, matrix, scanline_min_idx, lineSpacing, boundary, cutList, extrusionWidth);
|
||||
}
|
||||
|
||||
|
||||
void generateZigZagInfill_noEndPieces(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, int infillOverlap, double rotation)
|
||||
{
|
||||
if (in_outline.size() == 0) return;
|
||||
Polygons outline = in_outline.offset(extrusionWidth * infillOverlap / 100 - extrusionWidth / 2);
|
||||
if (outline.size() == 0) return;
|
||||
|
||||
PointMatrix matrix(rotation);
|
||||
|
||||
outline.applyMatrix(matrix);
|
||||
|
||||
auto addLine = [&](Point from, Point to)
|
||||
{
|
||||
PolygonRef p = result.newPoly();
|
||||
p.add(matrix.unapply(from));
|
||||
p.add(matrix.unapply(to));
|
||||
};
|
||||
|
||||
AABB boundary(outline);
|
||||
|
||||
int scanline_min_idx = boundary.min.X / lineSpacing;
|
||||
int lineCount = (boundary.max.X + (lineSpacing - 1)) / lineSpacing - scanline_min_idx;
|
||||
|
||||
std::vector<std::vector<int64_t> > cutList; // mapping from scanline to all intersections with polygon segments
|
||||
|
||||
for(int n=0; n<lineCount; n++)
|
||||
cutList.push_back(std::vector<int64_t>());
|
||||
for(unsigned int polyNr=0; polyNr < outline.size(); polyNr++)
|
||||
{
|
||||
std::vector<Point> firstBoundarySegment;
|
||||
std::vector<Point> boundarySegment;
|
||||
|
||||
bool isFirstBoundarySegment = true;
|
||||
bool firstBoundarySegmentEndsInEven;
|
||||
|
||||
bool isEvenScanSegment = false;
|
||||
|
||||
|
||||
Point p0 = outline[polyNr][outline[polyNr].size()-1];
|
||||
for(unsigned int i=0; i < outline[polyNr].size(); i++)
|
||||
{
|
||||
Point p1 = outline[polyNr][i];
|
||||
int64_t xMin = p1.X, xMax = p0.X;
|
||||
if (xMin == xMax) {
|
||||
p0 = p1;
|
||||
continue;
|
||||
}
|
||||
if (xMin > xMax) { xMin = p0.X; xMax = p1.X; }
|
||||
|
||||
int scanline_idx0 = (p0.X + ((p0.X > 0)? -1 : -lineSpacing)) / lineSpacing; // -1 cause a linesegment on scanline x counts as belonging to scansegment x-1 ...
|
||||
int scanline_idx1 = (p1.X + ((p1.X > 0)? -1 : -lineSpacing)) / lineSpacing; // -linespacing because a line between scanline -n and -n-1 belongs to scansegment -n-1 (for n=positive natural number)
|
||||
int direction = 1;
|
||||
if (p0.X > p1.X)
|
||||
{
|
||||
direction = -1;
|
||||
scanline_idx1 += 1; // only consider the scanlines in between the scansegments
|
||||
} else scanline_idx0 += 1; // only consider the scanlines in between the scansegments
|
||||
|
||||
|
||||
if (isFirstBoundarySegment) firstBoundarySegment.push_back(p0);
|
||||
else boundarySegment.push_back(p0);
|
||||
for(int scanline_idx = scanline_idx0; scanline_idx != scanline_idx1+direction; scanline_idx+=direction)
|
||||
{
|
||||
int x = scanline_idx * lineSpacing;
|
||||
int y = p1.Y + (p0.Y - p1.Y) * (x - p1.X) / (p0.X - p1.X);
|
||||
cutList[scanline_idx - scanline_min_idx].push_back(y);
|
||||
|
||||
|
||||
bool last_isEvenScanSegment = isEvenScanSegment;
|
||||
if (scanline_idx % 2 == 0) isEvenScanSegment = true;
|
||||
else isEvenScanSegment = false;
|
||||
|
||||
if (!isFirstBoundarySegment)
|
||||
{
|
||||
if (last_isEvenScanSegment && !isEvenScanSegment)
|
||||
{ // add whole boundarySegment (including the just obtained point)
|
||||
for (unsigned int p = 1; p < boundarySegment.size(); p++)
|
||||
{
|
||||
addLine(boundarySegment[p-1], boundarySegment[p]);
|
||||
}
|
||||
addLine(boundarySegment[boundarySegment.size()-1], Point(x,y));
|
||||
boundarySegment.clear();
|
||||
}
|
||||
else if (isEvenScanSegment) // we are either in an end piece or an uneven boundary segment
|
||||
{
|
||||
boundarySegment.clear();
|
||||
boundarySegment.emplace_back(x,y);
|
||||
} else
|
||||
boundarySegment.clear();
|
||||
|
||||
}
|
||||
|
||||
if (isFirstBoundarySegment)
|
||||
{
|
||||
firstBoundarySegment.emplace_back(x,y);
|
||||
firstBoundarySegmentEndsInEven = isEvenScanSegment;
|
||||
isFirstBoundarySegment = false;
|
||||
boundarySegment.emplace_back(x,y);
|
||||
}
|
||||
|
||||
}
|
||||
if (!isFirstBoundarySegment && isEvenScanSegment)
|
||||
boundarySegment.push_back(p1);
|
||||
|
||||
|
||||
p0 = p1;
|
||||
}
|
||||
|
||||
if (!isFirstBoundarySegment && isEvenScanSegment && !firstBoundarySegmentEndsInEven)
|
||||
{
|
||||
for (unsigned int i = 1; i < firstBoundarySegment.size() ; i++)
|
||||
addLine(firstBoundarySegment[i-1], firstBoundarySegment[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
addLineInfill(result, matrix, scanline_min_idx, lineSpacing, boundary, cutList, extrusionWidth);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}//namespace cura
|
||||
|
||||
+7
-4
@@ -7,10 +7,13 @@
|
||||
namespace cura {
|
||||
|
||||
void generateConcentricInfill(Polygons outline, Polygons& result, int inset_value);
|
||||
void generateAutomaticInfill(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, int infillOverlap, double rotation);
|
||||
void generateGridInfill(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, int infillOverlap, double rotation);
|
||||
void generateLineInfill(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, int infillOverlap, double rotation);
|
||||
|
||||
void generateConcentricInfillDense(Polygons outline, Polygons& result, Polygons* in_between, int extrusionWidth, bool avoidOverlappingPerimeters);
|
||||
void generateGridInfill(const Polygons& in_outline, int outlineOffset, Polygons& result, int extrusionWidth, int lineSpacing, int infillOverlap, double rotation);
|
||||
void generateTriangleInfill(const Polygons& in_outline, int outlineOffset, Polygons& result, int extrusionWidth, int lineSpacing, int infillOverlap, double rotation);
|
||||
void generateLineInfill(const Polygons& in_outline, int outlineOffset, Polygons& result, int extrusionWidth, int lineSpacing, int infillOverlap, double rotation);
|
||||
void generateZigZagInfill(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, int infillOverlap, double rotation, bool connect_zigzags, bool use_endPieces);
|
||||
void generateZigZagInfill_endPieces(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, int infillOverlap, double rotation, bool connect_zigzags);
|
||||
void generateZigZagInfill_noEndPieces(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, int infillOverlap, double rotation);
|
||||
}//namespace cura
|
||||
|
||||
#endif//INFILL_H
|
||||
|
||||
+18
-6
@@ -1,12 +1,13 @@
|
||||
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
|
||||
#include "inset.h"
|
||||
#include "polygonOptimizer.h"
|
||||
|
||||
#include "utils/polygonUtils.h"
|
||||
namespace cura {
|
||||
|
||||
void generateInsets(SliceLayerPart* part, int offset, int insetCount)
|
||||
void generateInsets(SliceLayerPart* part, int line_width_0, int line_width_x, int insetCount, bool avoidOverlappingPerimeters)
|
||||
{
|
||||
part->combBoundery = part->outline.offset(-offset);
|
||||
int combBoundaryInset = line_width_x/2; // hard coded value
|
||||
part->combBoundery = part->outline.offset(-combBoundaryInset);
|
||||
if (insetCount == 0)
|
||||
{
|
||||
part->insets.push_back(part->outline);
|
||||
@@ -16,7 +17,17 @@ void generateInsets(SliceLayerPart* part, int offset, int insetCount)
|
||||
for(int i=0; i<insetCount; i++)
|
||||
{
|
||||
part->insets.push_back(Polygons());
|
||||
part->insets[i] = part->outline.offset(-offset * i - offset/2);
|
||||
if (i == 0)
|
||||
{
|
||||
offsetSafe(part->outline, - line_width_x/2, line_width_x, part->insets[i], avoidOverlappingPerimeters);
|
||||
} else if (i == 1)
|
||||
{
|
||||
offsetExtrusionWidth(part->insets[i-1], true, line_width_0, part->insets[i], &part->perimeterGaps, avoidOverlappingPerimeters);
|
||||
} else
|
||||
{
|
||||
offsetExtrusionWidth(part->insets[i-1], true, line_width_x, part->insets[i], &part->perimeterGaps, avoidOverlappingPerimeters);
|
||||
}
|
||||
|
||||
optimizePolygons(part->insets[i]);
|
||||
if (part->insets[i].size() < 1)
|
||||
{
|
||||
@@ -26,11 +37,12 @@ void generateInsets(SliceLayerPart* part, int offset, int insetCount)
|
||||
}
|
||||
}
|
||||
|
||||
void generateInsets(SliceLayer* layer, int offset, int insetCount)
|
||||
|
||||
void generateInsets(SliceLayer* layer, int line_width_0, int line_width_x, int insetCount, bool avoidOverlappingPerimeters)
|
||||
{
|
||||
for(unsigned int partNr = 0; partNr < layer->parts.size(); partNr++)
|
||||
{
|
||||
generateInsets(&layer->parts[partNr], offset, insetCount);
|
||||
generateInsets(&layer->parts[partNr], line_width_0, line_width_x, insetCount, avoidOverlappingPerimeters);
|
||||
}
|
||||
|
||||
//Remove the parts which did not generate an inset. As these parts are too small to print,
|
||||
|
||||
+22
-3
@@ -4,11 +4,30 @@
|
||||
|
||||
#include "sliceDataStorage.h"
|
||||
|
||||
namespace cura {
|
||||
namespace cura
|
||||
{
|
||||
|
||||
void generateInsets(SliceLayerPart* part, int offset, int insetCount);
|
||||
/*!
|
||||
* Generates the insets / perimeters for a single layer part.
|
||||
*
|
||||
* \param part The part for which to generate the insets.
|
||||
* \param line_width_0 Line width of the outer wall
|
||||
* \param line_width_x Line width of other walls
|
||||
* \param insetCount The number of insets to to generate
|
||||
* \param avoidOverlappingPerimeters Whether to remove the parts of two consecutive perimeters where they have overlap (and store the gaps thus created in the \p part)
|
||||
*/
|
||||
void generateInsets(SliceLayerPart* part, int line_width_0, int line_width_x, int insetCount, bool avoidOverlappingPerimeters);
|
||||
|
||||
void generateInsets(SliceLayer* layer, int offset, int insetCount);
|
||||
/*!
|
||||
* Generates the insets / perimeters for all parts in a layer.
|
||||
*
|
||||
* \param layer The layer for which to generate the insets.
|
||||
* \param line_width_0 Line width of the outer wall
|
||||
* \param line_width_x Line width of other walls
|
||||
* \param insetCount The number of insets to to generate
|
||||
* \param avoidOverlappingPerimeters Whether to remove the parts of two consecutive perimeters where they have overlap (and store the gaps thus created in the \p part)
|
||||
*/
|
||||
void generateInsets(SliceLayer* layer, int line_width_0, int line_width_x, int insetCount, bool avoidOverlappingPerimeters);
|
||||
|
||||
}//namespace cura
|
||||
|
||||
|
||||
+21
-29
@@ -18,11 +18,11 @@ It's also the first step that stores the result in the "data storage" so all oth
|
||||
|
||||
namespace cura {
|
||||
|
||||
void createLayerWithParts(SliceLayer& storageLayer, SlicerLayer* layer, int unionAllType)
|
||||
void createLayerWithParts(SliceLayer& storageLayer, SlicerLayer* layer, bool union_layers, bool union_all_remove_holes)
|
||||
{
|
||||
storageLayer.openLines = layer->openPolygonList;
|
||||
storageLayer.openLines = layer->openPolygons;
|
||||
|
||||
if (unionAllType & FIX_HORRIBLE_UNION_ALL_TYPE_B)
|
||||
if (union_all_remove_holes)
|
||||
{
|
||||
for(unsigned int i=0; i<layer->polygonList.size(); i++)
|
||||
{
|
||||
@@ -31,32 +31,26 @@ void createLayerWithParts(SliceLayer& storageLayer, SlicerLayer* layer, int unio
|
||||
}
|
||||
}
|
||||
|
||||
vector<Polygons> result;
|
||||
if (unionAllType & FIX_HORRIBLE_UNION_ALL_TYPE_C)
|
||||
result = layer->polygonList.offset(1000).splitIntoParts(unionAllType);
|
||||
else
|
||||
result = layer->polygonList.splitIntoParts(unionAllType);
|
||||
std::vector<Polygons> result;
|
||||
result = layer->polygonList.splitIntoParts(union_layers || union_all_remove_holes);
|
||||
for(unsigned int i=0; i<result.size(); i++)
|
||||
{
|
||||
storageLayer.parts.push_back(SliceLayerPart());
|
||||
if (unionAllType & FIX_HORRIBLE_UNION_ALL_TYPE_C)
|
||||
{
|
||||
storageLayer.parts[i].outline.add(result[i][0]);
|
||||
storageLayer.parts[i].outline = storageLayer.parts[i].outline.offset(-1000);
|
||||
}else
|
||||
storageLayer.parts[i].outline = result[i];
|
||||
storageLayer.parts[i].outline = result[i];
|
||||
storageLayer.parts[i].boundaryBox.calculate(storageLayer.parts[i].outline);
|
||||
}
|
||||
}
|
||||
|
||||
void createLayerParts(SliceVolumeStorage& storage, Slicer* slicer, int unionAllType)
|
||||
void createLayerParts(SliceMeshStorage& storage, Slicer* slicer, bool union_layers, bool union_all_remove_holes)
|
||||
{
|
||||
for(unsigned int layerNr = 0; layerNr < slicer->layers.size(); layerNr++)
|
||||
for(unsigned int layer_nr = 0; layer_nr < slicer->layers.size(); layer_nr++)
|
||||
{
|
||||
storage.layers.push_back(SliceLayer());
|
||||
storage.layers[layerNr].sliceZ = slicer->layers[layerNr].z;
|
||||
storage.layers[layerNr].printZ = slicer->layers[layerNr].z;
|
||||
createLayerWithParts(storage.layers[layerNr], &slicer->layers[layerNr], unionAllType);
|
||||
storage.layers[layer_nr].sliceZ = slicer->layers[layer_nr].z;
|
||||
storage.layers[layer_nr].printZ = slicer->layers[layer_nr].z;
|
||||
createLayerWithParts(storage.layers[layer_nr], &slicer->layers[layer_nr], union_layers, union_all_remove_holes);
|
||||
|
||||
logProgress("layerparts", layer_nr + 1, slicer->layers.size());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,23 +58,21 @@ void dumpLayerparts(SliceDataStorage& storage, const char* filename)
|
||||
{
|
||||
FILE* out = fopen(filename, "w");
|
||||
fprintf(out, "<!DOCTYPE html><html><body>");
|
||||
Point3 modelSize = storage.modelSize;
|
||||
Point3 modelMin = storage.modelMin;
|
||||
Point3 modelSize = storage.model_size;
|
||||
Point3 modelMin = storage.model_min;
|
||||
|
||||
for(unsigned int volumeIdx=0; volumeIdx<storage.volumes.size(); volumeIdx++)
|
||||
for(SliceMeshStorage& mesh : storage.meshes)
|
||||
{
|
||||
for(unsigned int layerNr=0;layerNr<storage.volumes[volumeIdx].layers.size(); layerNr++)
|
||||
for(SliceLayer& layer : mesh.layers)
|
||||
{
|
||||
fprintf(out, "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" style=\"width: 500px; height:500px\">\n");
|
||||
SliceLayer* layer = &storage.volumes[volumeIdx].layers[layerNr];
|
||||
for(unsigned int i=0;i<layer->parts.size();i++)
|
||||
for(SliceLayerPart& part : layer.parts)
|
||||
{
|
||||
SliceLayerPart* part = &layer->parts[i];
|
||||
for(unsigned int j=0;j<part->outline.size();j++)
|
||||
for(unsigned int j=0;j<part.outline.size();j++)
|
||||
{
|
||||
fprintf(out, "<polygon points=\"");
|
||||
for(unsigned int k=0;k<part->outline[j].size();k++)
|
||||
fprintf(out, "%f,%f ", float(part->outline[j][k].X - modelMin.x)/modelSize.x*500, float(part->outline[j][k].Y - modelMin.y)/modelSize.y*500);
|
||||
for(unsigned int k=0;k<part.outline[j].size();k++)
|
||||
fprintf(out, "%f,%f ", float(part.outline[j][k].X - modelMin.x)/modelSize.x*500, float(part.outline[j][k].Y - modelMin.y)/modelSize.y*500);
|
||||
if (j == 0)
|
||||
fprintf(out, "\" style=\"fill:gray; stroke:black;stroke-width:1\" />\n");
|
||||
else
|
||||
|
||||
+2
-2
@@ -19,9 +19,9 @@ It's also the first step that stores the result in the "data storage" so all oth
|
||||
|
||||
namespace cura {
|
||||
|
||||
void createLayerWithParts(SliceLayer& storageLayer, SlicerLayer* layer, int unionAllType);
|
||||
void createLayerWithParts(SliceLayer& storageLayer, SlicerLayer* layer, bool union_layers, bool union_all_remove_holes);
|
||||
|
||||
void createLayerParts(SliceVolumeStorage& storage, Slicer* slicer, int unionAllType);
|
||||
void createLayerParts(SliceMeshStorage& storage, Slicer* slicer, bool union_layers, bool union_all_remove_holes);
|
||||
|
||||
void dumpLayerparts(SliceDataStorage& storage, const char* filename);
|
||||
|
||||
|
||||
+119
-111
@@ -13,11 +13,12 @@
|
||||
|
||||
#include "utils/gettime.h"
|
||||
#include "utils/logoutput.h"
|
||||
#include "utils/string.h"
|
||||
#include "sliceDataStorage.h"
|
||||
|
||||
#include "modelFile/modelFile.h"
|
||||
#include "settings.h"
|
||||
#include "optimizedModel.h"
|
||||
#include "settingRegistry.h"
|
||||
#include "multiVolumes.h"
|
||||
#include "polygonOptimizer.h"
|
||||
#include "slicer.h"
|
||||
@@ -56,147 +57,154 @@ int main(int argc, char **argv)
|
||||
setpriority(PRIO_PROCESS, 0, 10);
|
||||
#endif
|
||||
|
||||
#ifndef DEBUG
|
||||
//Register the exception handling for arithmic exceptions, this prevents the "something went wrong" dialog on windows to pop up on a division by zero.
|
||||
signal(SIGFPE, signal_FPE);
|
||||
#endif
|
||||
|
||||
ConfigSettings config;
|
||||
fffProcessor processor(config);
|
||||
fffProcessor processor;
|
||||
std::vector<std::string> files;
|
||||
|
||||
cura::logError("Cura_SteamEngine version %s\n", VERSION);
|
||||
cura::logError("Copyright (C) 2014 David Braam\n");
|
||||
cura::logError("\n");
|
||||
cura::logError("This program is free software: you can redistribute it and/or modify\n");
|
||||
cura::logError("it under the terms of the GNU Affero General Public License as published by\n");
|
||||
cura::logError("the Free Software Foundation, either version 3 of the License, or\n");
|
||||
cura::logError("(at your option) any later version.\n");
|
||||
cura::logError("\n");
|
||||
cura::logError("This program is distributed in the hope that it will be useful,\n");
|
||||
cura::logError("but WITHOUT ANY WARRANTY; without even the implied warranty of\n");
|
||||
cura::logError("MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n");
|
||||
cura::logError("GNU Affero General Public License for more details.\n");
|
||||
cura::logError("\n");
|
||||
cura::logError("You should have received a copy of the GNU Affero General Public License\n");
|
||||
cura::logError("along with this program. If not, see <http://www.gnu.org/licenses/>.\n");
|
||||
logCopyright("Cura_SteamEngine version %s\n", VERSION);
|
||||
logCopyright("Copyright (C) 2014 David Braam\n");
|
||||
logCopyright("\n");
|
||||
logCopyright("This program is free software: you can redistribute it and/or modify\n");
|
||||
logCopyright("it under the terms of the GNU Affero General Public License as published by\n");
|
||||
logCopyright("the Free Software Foundation, either version 3 of the License, or\n");
|
||||
logCopyright("(at your option) any later version.\n");
|
||||
logCopyright("\n");
|
||||
logCopyright("This program is distributed in the hope that it will be useful,\n");
|
||||
logCopyright("but WITHOUT ANY WARRANTY; without even the implied warranty of\n");
|
||||
logCopyright("MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n");
|
||||
logCopyright("GNU Affero General Public License for more details.\n");
|
||||
logCopyright("\n");
|
||||
logCopyright("You should have received a copy of the GNU Affero General Public License\n");
|
||||
logCopyright("along with this program. If not, see <http://www.gnu.org/licenses/>.\n");
|
||||
|
||||
if(!config.readSettings()) {
|
||||
cura::logError("Default config '%s' not used\n", DEFAULT_CONFIG_PATH);
|
||||
}
|
||||
for(int argn = 1; argn < argc; argn++)
|
||||
cura::log("Arg: %s\n", argv[argn]);
|
||||
CommandSocket* commandSocket = NULL;
|
||||
std::string ip;
|
||||
int port = 49674;
|
||||
|
||||
for(int argn = 1; argn < argc; argn++)
|
||||
{
|
||||
char* str = argv[argn];
|
||||
if (str[0] == '-')
|
||||
{
|
||||
for(str++; *str; str++)
|
||||
if (str[1] == '-')
|
||||
{
|
||||
switch(*str)
|
||||
if (stringcasecompare(str, "--connect") == 0)
|
||||
{
|
||||
case 'h':
|
||||
print_usage();
|
||||
exit(1);
|
||||
case 'v':
|
||||
cura::increaseVerboseLevel();
|
||||
break;
|
||||
case 'p':
|
||||
cura::enableProgressLogging();
|
||||
break;
|
||||
case 'g':
|
||||
argn++;
|
||||
//Connect the GUI socket to the given port number.
|
||||
processor.guiConnect(atoi(argv[argn]));
|
||||
break;
|
||||
case 'b':
|
||||
argn++;
|
||||
//The binaryMeshBlob is depricated and will be removed in the future.
|
||||
binaryMeshBlob = fopen(argv[argn], "rb");
|
||||
break;
|
||||
case 'o':
|
||||
argn++;
|
||||
if (!processor.setTargetFile(argv[argn]))
|
||||
{
|
||||
cura::logError("Failed to open %s for output.\n", argv[argn]);
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
case 'c':
|
||||
{
|
||||
// Read a config file from the given path
|
||||
argn++;
|
||||
if(!config.readSettings(argv[argn])) {
|
||||
cura::logError("Failed to read config '%s'\n", argv[argn]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 's':
|
||||
{
|
||||
//Parse the given setting and store it.
|
||||
argn++;
|
||||
char* valuePtr = strchr(argv[argn], '=');
|
||||
if (valuePtr)
|
||||
{
|
||||
*valuePtr++ = '\0';
|
||||
commandSocket = new CommandSocket(&processor);
|
||||
|
||||
if (!config.setSetting(argv[argn], valuePtr))
|
||||
cura::logError("Setting not found: %s %s\n", argv[argn], valuePtr);
|
||||
}
|
||||
std::string ip_port(argv[argn + 1]);
|
||||
if (ip_port.find(':') != std::string::npos)
|
||||
{
|
||||
ip = ip_port.substr(0, ip_port.find(':'));
|
||||
port = std::stoi(ip_port.substr(ip_port.find(':') + 1).data());
|
||||
}
|
||||
break;
|
||||
case 'm':
|
||||
//Read the given rotation/scale matrix
|
||||
argn++;
|
||||
sscanf(argv[argn], "%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf",
|
||||
&config.matrix.m[0][0], &config.matrix.m[0][1], &config.matrix.m[0][2],
|
||||
&config.matrix.m[1][0], &config.matrix.m[1][1], &config.matrix.m[1][2],
|
||||
&config.matrix.m[2][0], &config.matrix.m[2][1], &config.matrix.m[2][2]);
|
||||
break;
|
||||
case '-':
|
||||
|
||||
argn += 1;
|
||||
}
|
||||
else if (stringcasecompare(str, "--") == 0)
|
||||
{
|
||||
try {
|
||||
//Catch all exceptions, this prevents the "something went wrong" dialog on windows to pop up on a thrown exception.
|
||||
// Only ClipperLib currently throws exceptions. And only in case that it makes an internal error.
|
||||
if (files.size() > 0)
|
||||
processor.processFile(files);
|
||||
processor.processFiles(files);
|
||||
files.clear();
|
||||
}catch(...){
|
||||
cura::logError("Unknown exception\n");
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
cura::logError("Unknown option: %c\n", *str);
|
||||
break;
|
||||
}else{
|
||||
cura::logError("Unknown option: %s\n", str);
|
||||
}
|
||||
}else{
|
||||
for(str++; *str; str++)
|
||||
{
|
||||
switch(*str)
|
||||
{
|
||||
case 'h':
|
||||
print_usage();
|
||||
exit(1);
|
||||
case 'v':
|
||||
cura::increaseVerboseLevel();
|
||||
break;
|
||||
case 'j':
|
||||
argn++;
|
||||
if (!SettingRegistry::getInstance()->loadJSON(argv[argn]))
|
||||
{
|
||||
cura::logError("ERROR: Failed to load json file: %s\n", argv[argn]);
|
||||
}
|
||||
break;
|
||||
case 'p':
|
||||
cura::enableProgressLogging();
|
||||
break;
|
||||
case 'o':
|
||||
argn++;
|
||||
if (!processor.setTargetFile(argv[argn]))
|
||||
{
|
||||
cura::logError("Failed to open %s for output.\n", argv[argn]);
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
case 's':
|
||||
{
|
||||
//Parse the given setting and store it.
|
||||
argn++;
|
||||
char* valuePtr = strchr(argv[argn], '=');
|
||||
if (valuePtr)
|
||||
{
|
||||
*valuePtr++ = '\0';
|
||||
|
||||
processor.setSetting(argv[argn], valuePtr);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
cura::logError("Unknown option: %c\n", *str);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}else{
|
||||
if (argv[argn][0] == '$')
|
||||
{
|
||||
try {
|
||||
//Catch all exceptions, this prevents the "something went wrong" dialog on windows to pop up on a thrown exception.
|
||||
// Only ClipperLib currently throws exceptions. And only in case that it makes an internal error.
|
||||
std::vector<std::string> tmp;
|
||||
tmp.push_back(argv[argn]);
|
||||
processor.processFile(tmp);
|
||||
}catch(...){
|
||||
cura::logError("Unknown exception\n");
|
||||
exit(1);
|
||||
}
|
||||
}else{
|
||||
files.push_back(argv[argn]);
|
||||
}
|
||||
files.push_back(argv[argn]);
|
||||
}
|
||||
}
|
||||
try {
|
||||
//Catch all exceptions, this prevents the "something went wrong" dialog on windows to pop up on a thrown exception.
|
||||
// Only ClipperLib currently throws exceptions. And only in case that it makes an internal error.
|
||||
if (files.size() > 0)
|
||||
processor.processFile(files);
|
||||
}catch(...){
|
||||
cura::logError("Unknown exception\n");
|
||||
exit(1);
|
||||
|
||||
if (!SettingRegistry::getInstance()->settingsLoaded())
|
||||
{
|
||||
//If no json file has been loaded, try to load the default.
|
||||
if (!SettingRegistry::getInstance()->loadJSON("fdmprinter.json"))
|
||||
{
|
||||
logError("ERROR: Failed to load json file: fdmprinter.json\n");
|
||||
}
|
||||
}
|
||||
//Finalize the processor, this adds the end.gcode. And reports statistics.
|
||||
processor.finalize();
|
||||
|
||||
if(commandSocket)
|
||||
{
|
||||
commandSocket->connect(ip, port);
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifndef DEBUG
|
||||
try {
|
||||
#endif
|
||||
//Catch all exceptions, this prevents the "something went wrong" dialog on windows to pop up on a thrown exception.
|
||||
// Only ClipperLib currently throws exceptions. And only in case that it makes an internal error.
|
||||
if (files.size() > 0)
|
||||
processor.processFiles(files);
|
||||
#ifndef DEBUG
|
||||
}catch(...){
|
||||
cura::logError("Unknown exception\n");
|
||||
exit(1);
|
||||
}
|
||||
#endif
|
||||
//Finalize the processor, this adds the end.gcode. And reports statistics.
|
||||
processor.finalize();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
+193
@@ -0,0 +1,193 @@
|
||||
#include "mesh.h"
|
||||
#include "utils/logoutput.h"
|
||||
|
||||
|
||||
const int vertex_meld_distance = MM2INT(0.03);
|
||||
static inline uint32_t pointHash(Point3& p)
|
||||
{
|
||||
return ((p.x + vertex_meld_distance/2) / vertex_meld_distance) ^ (((p.y + vertex_meld_distance/2) / vertex_meld_distance) << 10) ^ (((p.z + vertex_meld_distance/2) / vertex_meld_distance) << 20);
|
||||
}
|
||||
|
||||
Mesh::Mesh(SettingsBase* parent)
|
||||
: SettingsBase(parent)
|
||||
{
|
||||
}
|
||||
|
||||
void Mesh::addFace(Point3& v0, Point3& v1, Point3& v2)
|
||||
{
|
||||
int vi0 = findIndexOfVertex(v0);
|
||||
int vi1 = findIndexOfVertex(v1);
|
||||
int vi2 = findIndexOfVertex(v2);
|
||||
if (vi0 == vi1 || vi1 == vi2 || vi0 == vi2) return; // the face has two vertices which get assigned the same location. Don't add the face.
|
||||
|
||||
int idx = faces.size(); // index of face to be added
|
||||
faces.emplace_back();
|
||||
MeshFace& face = faces[idx];
|
||||
face.vertex_index[0] = vi0;
|
||||
face.vertex_index[1] = vi1;
|
||||
face.vertex_index[2] = vi2;
|
||||
vertices[face.vertex_index[0]].connected_faces.push_back(idx);
|
||||
vertices[face.vertex_index[1]].connected_faces.push_back(idx);
|
||||
vertices[face.vertex_index[2]].connected_faces.push_back(idx);
|
||||
}
|
||||
|
||||
void Mesh::clear()
|
||||
{
|
||||
faces.clear();
|
||||
vertices.clear();
|
||||
vertex_hash_map.clear();
|
||||
}
|
||||
|
||||
void Mesh::finish()
|
||||
{
|
||||
// Finish up the mesh, clear the vertex_hash_map, as it's no longer needed from this point on and uses quite a bit of memory.
|
||||
vertex_hash_map.clear();
|
||||
|
||||
// For each face, store which other face is connected with it.
|
||||
for(unsigned int i=0; i<faces.size(); i++)
|
||||
{
|
||||
MeshFace& face = faces[i];
|
||||
face.connected_face_index[0] = getFaceIdxWithPoints(face.vertex_index[0], face.vertex_index[1], i); // faces are connected via the outside
|
||||
face.connected_face_index[1] = getFaceIdxWithPoints(face.vertex_index[1], face.vertex_index[2], i);
|
||||
face.connected_face_index[2] = getFaceIdxWithPoints(face.vertex_index[2], face.vertex_index[0], i);
|
||||
}
|
||||
}
|
||||
|
||||
Point3 Mesh::min()
|
||||
{
|
||||
if (vertices.size() < 1)
|
||||
return Point3(0, 0, 0);
|
||||
Point3 ret = vertices[0].p;
|
||||
for(unsigned int i=0; i<vertices.size(); i++)
|
||||
{
|
||||
ret.x = std::min(ret.x, vertices[i].p.x);
|
||||
ret.y = std::min(ret.y, vertices[i].p.y);
|
||||
ret.z = std::min(ret.z, vertices[i].p.z);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
Point3 Mesh::max()
|
||||
{
|
||||
if (vertices.size() < 1)
|
||||
return Point3(0, 0, 0);
|
||||
Point3 ret = vertices[0].p;
|
||||
for(unsigned int i=0; i<vertices.size(); i++)
|
||||
{
|
||||
ret.x = std::max(ret.x, vertices[i].p.x);
|
||||
ret.y = std::max(ret.y, vertices[i].p.y);
|
||||
ret.z = std::max(ret.z, vertices[i].p.z);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int Mesh::findIndexOfVertex(Point3& v)
|
||||
{
|
||||
uint32_t hash = pointHash(v);
|
||||
|
||||
for(unsigned int idx = 0; idx < vertex_hash_map[hash].size(); idx++)
|
||||
{
|
||||
if ((vertices[vertex_hash_map[hash][idx]].p - v).testLength(vertex_meld_distance))
|
||||
{
|
||||
return vertex_hash_map[hash][idx];
|
||||
}
|
||||
}
|
||||
vertex_hash_map[hash].push_back(vertices.size());
|
||||
vertices.emplace_back(v);
|
||||
return vertices.size() - 1;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the index of the 'other' face connected to the edge between vertices with indices idx0 and idx1.
|
||||
In case more than two faces are connected via the same edge, the next face in a counter-clockwise ordering (looking from idx1 to idx0) is returned.
|
||||
|
||||
\cond DOXYGEN_EXCLUDE
|
||||
[NON-RENDERED COMENTS]
|
||||
For two faces abc and abd with normals n and m, we have that:
|
||||
\f{eqnarray*}{
|
||||
n &=& \frac{ab \times ac}{\|ab \times ac\|} \\
|
||||
m &=& \frac{ab \times ad}{\|ab \times ad\|} \\
|
||||
n \times m &=& \|n\| \cdot \|m\| \mathbf{p} \sin \alpha \\
|
||||
&& (\mathbf{p} \perp n \wedge \mathbf{p} \perp m) \\
|
||||
\sin \alpha &=& \|n \times m \|
|
||||
&=& \left\| \frac{(ab \times ac) \times (ab \times ad)}{\|ab \times ac\| \cdot \|ab \times ad\|} \right\| \\
|
||||
&=& \left\| \frac{ (ab \cdot (ac \times ad)) ab }{\|ab \times ac\| \cdot \|ab \times ad\|} \right\| \\
|
||||
&=& \frac{ (ab \cdot (ac \times ad)) \left\| ab \right\| }{\|ab\| \|ac\| \sin bac \cdot \|ab\| \|ad\| \sin bad} \\
|
||||
&=& \frac{ ab \cdot (ac \times ad) }{\|ab\| \|ac\| \|ad\| \sin bac \sin bad} \\
|
||||
\f}}
|
||||
\endcond
|
||||
|
||||
See <a href="http://stackoverflow.com/questions/14066933/direct-way-of-computing-clockwise-angle-between-2-vectors">Direct way of computing clockwise angle between 2 vectors</a>
|
||||
|
||||
|
||||
*/
|
||||
int Mesh::getFaceIdxWithPoints(int idx0, int idx1, int notFaceIdx)
|
||||
{
|
||||
std::vector<int> candidateFaces; // in case more than two faces meet at an edge, multiple candidates are generated
|
||||
int notFaceVertexIdx = -1; // index of the third vertex of the face corresponding to notFaceIdx
|
||||
for(int f : vertices[idx0].connected_faces) // search through all faces connected to the first vertex and find those that are also connected to the second
|
||||
{
|
||||
if (f == notFaceIdx)
|
||||
{
|
||||
for (int i = 0; i<3; i++) // find the vertex which is not idx0 or idx1
|
||||
if (faces[f].vertex_index[i] != idx0 && faces[f].vertex_index[i] != idx1)
|
||||
notFaceVertexIdx = faces[f].vertex_index[i];
|
||||
continue;
|
||||
}
|
||||
if ( faces[f].vertex_index[0] == idx1 // && faces[f].vertex_index[1] == idx0 // next face should have the right direction!
|
||||
|| faces[f].vertex_index[1] == idx1 // && faces[f].vertex_index[2] == idx0
|
||||
|| faces[f].vertex_index[2] == idx1 // && faces[f].vertex_index[0] == idx0
|
||||
) candidateFaces.push_back(f);
|
||||
|
||||
}
|
||||
|
||||
if (candidateFaces.size() == 0) { cura::logError("Couldn't find face connected to face %i.\n", notFaceIdx); return -1; }
|
||||
if (candidateFaces.size() == 1) { return candidateFaces[0]; }
|
||||
|
||||
|
||||
if (notFaceVertexIdx < 0) { cura::logError("Couldn't find third point on face %i.\n", notFaceIdx); return -1; }
|
||||
|
||||
if (candidateFaces.size() % 2 == 0) cura::log("Warning! Edge with uneven number of faces connecting it!(%i)\n", candidateFaces.size()+1);
|
||||
|
||||
FPoint3 vn = vertices[idx1].p - vertices[idx0].p;
|
||||
FPoint3 n = vn / vn.vSize(); // the normal of the plane in which all normals of faces connected to the edge lie => the normalized normal
|
||||
FPoint3 v0 = vertices[idx1].p - vertices[idx0].p;
|
||||
|
||||
// the normals below are abnormally directed! : these normals all point counterclockwise (viewed from idx1 to idx0) from the face, irrespective of the direction of the face.
|
||||
FPoint3 n0 = FPoint3(vertices[notFaceVertexIdx].p - vertices[idx0].p).cross(v0);
|
||||
|
||||
if (n0.vSize() <= 0) cura::log("Warning! Face %i has zero area!", notFaceIdx);
|
||||
|
||||
double smallestAngle = 1000; // more then 2 PI (impossible angle)
|
||||
int bestIdx = -1;
|
||||
|
||||
for (int candidateFace : candidateFaces)
|
||||
{
|
||||
int candidateVertex;
|
||||
{// find third vertex belonging to the face (besides idx0 and idx1)
|
||||
for (candidateVertex = 0; candidateVertex<3; candidateVertex++)
|
||||
if (faces[candidateFace].vertex_index[candidateVertex] != idx0 && faces[candidateFace].vertex_index[candidateVertex] != idx1)
|
||||
break;
|
||||
}
|
||||
|
||||
FPoint3 v1 = vertices[candidateVertex].p -vertices[idx0].p;
|
||||
FPoint3 n1 = v1.cross(v0);
|
||||
|
||||
double dot = n0 * n1;
|
||||
double det = n * n0.cross(n1);
|
||||
double angle = std::atan2(det, dot);
|
||||
if (angle < 0) angle += 2*M_PI; // 0 <= angle < 2* M_PI
|
||||
|
||||
if (angle == 0)
|
||||
{
|
||||
cura::log("Warning! Overlapping faces: face %i and face %i.\n", notFaceIdx, candidateFace);
|
||||
std::cerr<< n.vSize() <<"; "<<n1.vSize()<<";"<<n0.vSize() <<std::endl;
|
||||
}
|
||||
if (angle < smallestAngle)
|
||||
{
|
||||
smallestAngle = angle;
|
||||
bestIdx = candidateFace;
|
||||
}
|
||||
}
|
||||
if (bestIdx < 0) cura::logError("Couldn't find face connected to face %i.\n", notFaceIdx);
|
||||
return bestIdx;
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
#ifndef MESH_H
|
||||
#define MESH_H
|
||||
|
||||
#include "settings.h"
|
||||
|
||||
/*!
|
||||
Vertex type to be used in a Mesh.
|
||||
|
||||
Keeps track of which faces connect to it.
|
||||
*/
|
||||
class MeshVertex
|
||||
{
|
||||
public:
|
||||
Point3 p; //!< location of the vertex
|
||||
std::vector<uint32_t> connected_faces; //!< list of the indices of connected faces
|
||||
|
||||
MeshVertex(Point3 p) : p(p) {} //!< doesn't set connected_faces
|
||||
};
|
||||
|
||||
/*! A MeshFace is a 3 dimensional model triangle with 3 points. These points are already converted to integers
|
||||
|
||||
A face has 3 connected faces, corresponding to its 3 edges.
|
||||
|
||||
Note that a correct model may have more than 2 faces connected via a single edge!
|
||||
In such a case the face_index stored in connected_face_index is the one connected via the outside; see ASCII art below:
|
||||
|
||||
: horizontal slice through vertical edge connected to four faces :
|
||||
|
||||
\verbatim
|
||||
[inside] x|
|
||||
x| <--+--- faces which contain each other in their connected_face_index fiels
|
||||
xxxxxxx| \|/
|
||||
-------+-------
|
||||
^ |xxxxxxx
|
||||
+-->|x
|
||||
| |x [inside]
|
||||
|
|
||||
faces which contain each other in their connected_face_index fiels
|
||||
\endverbatim
|
||||
*/
|
||||
class MeshFace
|
||||
{
|
||||
public:
|
||||
int vertex_index[3] = {-1}; //!< counter-clockwise ordering
|
||||
int connected_face_index[3]; //!< same ordering as vertex_index (connected_face 0 is connected via vertex 0 and 1, etc.)
|
||||
};
|
||||
|
||||
|
||||
/*!
|
||||
A Mesh is the most basic representation of a 3D model. It contains all the faces as MeshFaces.
|
||||
|
||||
See MeshFace for the specifics of how/when faces are connected.
|
||||
*/
|
||||
class Mesh : public SettingsBase // inherits settings
|
||||
{
|
||||
//! The vertex_hash_map stores a index reference of each vertex for the hash of that location. Allows for quick retrieval of points with the same location.
|
||||
std::map<uint32_t, std::vector<uint32_t> > vertex_hash_map;
|
||||
public:
|
||||
std::vector<MeshVertex> vertices;//!< list of all vertices in the mesh
|
||||
std::vector<MeshFace> faces; //!< list of all faces in the mesh
|
||||
|
||||
Mesh(SettingsBase* parent); //!< initializes the settings
|
||||
|
||||
void addFace(Point3& v0, Point3& v1, Point3& v2); //!< add a face to the mesh without settings it's connected_faces.
|
||||
void clear(); //!< clears all data
|
||||
void finish(); //!< complete the model : set the connected_face_index fields of the faces.
|
||||
|
||||
Point3 min(); //!< min (in x,y and z) vertex of the bounding box
|
||||
Point3 max(); //!< max (in x,y and z) vertex of the bounding box
|
||||
|
||||
private:
|
||||
int findIndexOfVertex(Point3& v); //!< find index of vertex close to the given point, or create a new vertex and return its index.
|
||||
/*!
|
||||
Get the index of the face connected to the face with index \p notFaceIdx, via vertices \p idx0 and \p idx1.
|
||||
In case multiple faces connect with the same edge, return the next counter-clockwise face when viewing from \p idx1 to \p idx0.
|
||||
*/
|
||||
int getFaceIdxWithPoints(int idx0, int idx1, int notFaceIdx);
|
||||
};
|
||||
|
||||
|
||||
#endif//MESH_H
|
||||
|
||||
@@ -25,10 +25,8 @@ void* fgets_(char* ptr, size_t len, FILE* f)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SimpleModel* loadModelSTL_ascii(SimpleModel *m,const char* filename, FMatrix3x3& matrix)
|
||||
bool loadModelSTL_ascii(Mesh* mesh, const char* filename, FMatrix3x3& matrix)
|
||||
{
|
||||
m->volumes.push_back(SimpleVolume());
|
||||
SimpleVolume* vol = &m->volumes[m->volumes.size()-1];
|
||||
FILE* f = fopen(filename, "rt");
|
||||
char buffer[1024];
|
||||
FPoint3 vertex;
|
||||
@@ -36,7 +34,7 @@ SimpleModel* loadModelSTL_ascii(SimpleModel *m,const char* filename, FMatrix3x3&
|
||||
Point3 v0(0,0,0), v1(0,0,0), v2(0,0,0);
|
||||
while(fgets_(buffer, sizeof(buffer), f))
|
||||
{
|
||||
if (sscanf(buffer, " vertex %lf %lf %lf", &vertex.x, &vertex.y, &vertex.z) == 3)
|
||||
if (sscanf(buffer, " vertex %f %f %f", &vertex.x, &vertex.y, &vertex.z) == 3)
|
||||
{
|
||||
n++;
|
||||
switch(n)
|
||||
@@ -49,17 +47,18 @@ SimpleModel* loadModelSTL_ascii(SimpleModel *m,const char* filename, FMatrix3x3&
|
||||
break;
|
||||
case 3:
|
||||
v2 = matrix.apply(vertex);
|
||||
vol->addFace(v0, v1, v2);
|
||||
mesh->addFace(v0, v1, v2);
|
||||
n = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
fclose(f);
|
||||
return m;
|
||||
mesh->finish();
|
||||
return true;
|
||||
}
|
||||
|
||||
SimpleModel* loadModelSTL_binary(SimpleModel *m,const char* filename, FMatrix3x3& matrix)
|
||||
bool loadModelSTL_binary(Mesh* mesh, const char* filename, FMatrix3x3& matrix)
|
||||
{
|
||||
FILE* f = fopen(filename, "rb");
|
||||
char buffer[80];
|
||||
@@ -68,120 +67,84 @@ SimpleModel* loadModelSTL_binary(SimpleModel *m,const char* filename, FMatrix3x3
|
||||
if (fread(buffer, 80, 1, f) != 1)
|
||||
{
|
||||
fclose(f);
|
||||
return nullptr;
|
||||
return false;
|
||||
}
|
||||
//Read the face count
|
||||
if (fread(&faceCount, sizeof(uint32_t), 1, f) != 1)
|
||||
{
|
||||
fclose(f);
|
||||
return nullptr;
|
||||
return false;
|
||||
}
|
||||
//For each face read:
|
||||
//float(x,y,z) = normal, float(X,Y,Z)*3 = vertexes, uint16_t = flags
|
||||
m->volumes.push_back(SimpleVolume());
|
||||
SimpleVolume* vol = &m->volumes[m->volumes.size()-1];
|
||||
if(vol == nullptr)
|
||||
{
|
||||
fclose(f);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for(unsigned int i=0;i<faceCount;i++)
|
||||
{
|
||||
if (fread(buffer, sizeof(float) * 3, 1, f) != 1)
|
||||
{
|
||||
fclose(f);
|
||||
return nullptr;
|
||||
return false;
|
||||
}
|
||||
float v[9];
|
||||
if (fread(v, sizeof(float) * 9, 1, f) != 1)
|
||||
{
|
||||
fclose(f);
|
||||
return nullptr;
|
||||
return false;
|
||||
}
|
||||
Point3 v0 = matrix.apply(FPoint3(v[0], v[1], v[2]));
|
||||
Point3 v1 = matrix.apply(FPoint3(v[3], v[4], v[5]));
|
||||
Point3 v2 = matrix.apply(FPoint3(v[6], v[7], v[8]));
|
||||
vol->addFace(v0, v1, v2);
|
||||
mesh->addFace(v0, v1, v2);
|
||||
if (fread(buffer, sizeof(uint16_t), 1, f) != 1)
|
||||
{
|
||||
fclose(f);
|
||||
return nullptr;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
fclose(f);
|
||||
return m;
|
||||
mesh->finish();
|
||||
return true;
|
||||
}
|
||||
|
||||
SimpleModel* loadModelSTL(SimpleModel *m,const char* filename, FMatrix3x3& matrix)
|
||||
bool loadModelSTL(Mesh* mesh, const char* filename, FMatrix3x3& matrix)
|
||||
{
|
||||
FILE* f = fopen(filename, "r");
|
||||
char buffer[6];
|
||||
if (f == nullptr)
|
||||
return nullptr;
|
||||
return false;
|
||||
|
||||
if (fread(buffer, 5, 1, f) != 1)
|
||||
{
|
||||
fclose(f);
|
||||
return nullptr;
|
||||
return false;
|
||||
}
|
||||
fclose(f);
|
||||
|
||||
buffer[5] = '\0';
|
||||
if (stringcasecompare(buffer, "solid") == 0)
|
||||
{
|
||||
SimpleModel* asciiModel = loadModelSTL_ascii(m, filename, matrix);
|
||||
if (!asciiModel)
|
||||
return nullptr;
|
||||
bool load_success = loadModelSTL_ascii(mesh, filename, matrix);
|
||||
if (!load_success)
|
||||
return false;
|
||||
|
||||
// This logic is used to handle the case where the file starts with
|
||||
// "solid" but is a binary file.
|
||||
if (m->volumes[m->volumes.size()-1].faces.size() < 1)
|
||||
if (mesh->faces.size() < 1)
|
||||
{
|
||||
m->volumes.erase(m->volumes.end() - 1);
|
||||
return loadModelSTL_binary(m, filename, matrix);
|
||||
mesh->clear();
|
||||
return loadModelSTL_binary(mesh, filename, matrix);
|
||||
}
|
||||
return asciiModel;
|
||||
return true;
|
||||
}
|
||||
return loadModelSTL_binary(m, filename, matrix);
|
||||
return loadModelSTL_binary(mesh, filename, matrix);
|
||||
}
|
||||
|
||||
SimpleModel* loadModelFromFile(SimpleModel *m,const char* filename, FMatrix3x3& matrix)
|
||||
bool loadMeshFromFile(PrintObject* object, const char* filename, FMatrix3x3& matrix)
|
||||
{
|
||||
const char* ext = strrchr(filename, '.');
|
||||
if (ext && stringcasecompare(ext, ".stl") == 0)
|
||||
if (ext && (strcmp(ext, ".stl") == 0 || strcmp(ext, ".STL") == 0))
|
||||
{
|
||||
return loadModelSTL(m,filename, matrix);
|
||||
object->meshes.emplace_back(object);
|
||||
return loadModelSTL(&object->meshes[object->meshes.size()-1], filename, matrix);
|
||||
}
|
||||
if (filename[0] == '#' && binaryMeshBlob != nullptr)
|
||||
{
|
||||
while(*filename == '#')
|
||||
{
|
||||
filename++;
|
||||
|
||||
m->volumes.push_back(SimpleVolume());
|
||||
SimpleVolume* vol = &m->volumes[m->volumes.size()-1];
|
||||
int32_t n, pNr = 0;
|
||||
if (fread(&n, 1, sizeof(int32_t), binaryMeshBlob) < 1)
|
||||
return nullptr;
|
||||
cura::log("Reading mesh from binary blob with %i vertexes\n", n);
|
||||
Point3 v[3];
|
||||
while(n)
|
||||
{
|
||||
float f[3];
|
||||
if (fread(f, 3, sizeof(float), binaryMeshBlob) < 1)
|
||||
return nullptr;
|
||||
FPoint3 fp(f[0], f[1], f[2]);
|
||||
v[pNr++] = matrix.apply(fp);
|
||||
if (pNr == 3)
|
||||
{
|
||||
vol->addFace(v[0], v[1], v[2]);
|
||||
pNr = 0;
|
||||
}
|
||||
n--;
|
||||
}
|
||||
}
|
||||
return m;
|
||||
}
|
||||
return nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
+66
-86
@@ -7,112 +7,92 @@ modelFile contains the model loaders for the slicer. The model loader turns any
|
||||
The format returned is a Model class with an array of faces, which have integer points with a resolution of 1 micron. Giving a maximum object size of 4 meters.
|
||||
**/
|
||||
|
||||
#include <vector>
|
||||
using std::vector;
|
||||
#include "../utils/intpoint.h"
|
||||
#include "../utils/floatpoint.h"
|
||||
#include "../mesh.h"
|
||||
|
||||
extern FILE* binaryMeshBlob;
|
||||
|
||||
#define SET_MIN(n, m) do { if ((m) < (n)) n = m; } while(0)
|
||||
#define SET_MAX(n, m) do { if ((m) > (n)) n = m; } while(0)
|
||||
|
||||
/* A SimpleFace is a 3 dimensional model triangle with 3 points. These points are already converted to integers */
|
||||
class SimpleFace
|
||||
//A PrintObject is a 3D model with 1 or more 3D meshes.
|
||||
class PrintObject : public SettingsBase
|
||||
{
|
||||
public:
|
||||
Point3 v[3];
|
||||
std::vector<Mesh> meshes;
|
||||
|
||||
SimpleFace(Point3& v0, Point3& v1, Point3& v2) { v[0] = v0; v[1] = v1; v[2] = v2; }
|
||||
};
|
||||
|
||||
/* A SimpleVolume is the most basic reprisentation of a 3D model. It contains all the faces as SimpleTriangles, with nothing fancy. */
|
||||
class SimpleVolume
|
||||
{
|
||||
public:
|
||||
vector<SimpleFace> faces;
|
||||
|
||||
void addFace(Point3& v0, Point3& v1, Point3& v2)
|
||||
PrintObject(SettingsBase* settings_base)
|
||||
: SettingsBase(settings_base)
|
||||
{
|
||||
faces.push_back(SimpleFace(v0, v1, v2));
|
||||
}
|
||||
|
||||
Point3 min()
|
||||
Point3 min() //! minimal corner of bounding box
|
||||
{
|
||||
if (faces.size() < 1)
|
||||
if (meshes.size() < 1)
|
||||
return Point3(0, 0, 0);
|
||||
Point3 ret = faces[0].v[0];
|
||||
for(unsigned int i=0; i<faces.size(); i++)
|
||||
Point3 ret = meshes[0].min();
|
||||
for(unsigned int i=1; i<meshes.size(); i++)
|
||||
{
|
||||
SET_MIN(ret.x, faces[i].v[0].x);
|
||||
SET_MIN(ret.y, faces[i].v[0].y);
|
||||
SET_MIN(ret.z, faces[i].v[0].z);
|
||||
SET_MIN(ret.x, faces[i].v[1].x);
|
||||
SET_MIN(ret.y, faces[i].v[1].y);
|
||||
SET_MIN(ret.z, faces[i].v[1].z);
|
||||
SET_MIN(ret.x, faces[i].v[2].x);
|
||||
SET_MIN(ret.y, faces[i].v[2].y);
|
||||
SET_MIN(ret.z, faces[i].v[2].z);
|
||||
Point3 v = meshes[i].min();
|
||||
ret.x = std::min(ret.x, v.x);
|
||||
ret.y = std::min(ret.y, v.y);
|
||||
ret.z = std::min(ret.z, v.z);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
Point3 max()
|
||||
Point3 max() //! maximal corner of bounding box
|
||||
{
|
||||
if (faces.size() < 1)
|
||||
if (meshes.size() < 1)
|
||||
return Point3(0, 0, 0);
|
||||
Point3 ret = faces[0].v[0];
|
||||
for(unsigned int i=0; i<faces.size(); i++)
|
||||
Point3 ret = meshes[0].max();
|
||||
for(unsigned int i=1; i<meshes.size(); i++)
|
||||
{
|
||||
SET_MAX(ret.x, faces[i].v[0].x);
|
||||
SET_MAX(ret.y, faces[i].v[0].y);
|
||||
SET_MAX(ret.z, faces[i].v[0].z);
|
||||
SET_MAX(ret.x, faces[i].v[1].x);
|
||||
SET_MAX(ret.y, faces[i].v[1].y);
|
||||
SET_MAX(ret.z, faces[i].v[1].z);
|
||||
SET_MAX(ret.x, faces[i].v[2].x);
|
||||
SET_MAX(ret.y, faces[i].v[2].y);
|
||||
SET_MAX(ret.z, faces[i].v[2].z);
|
||||
Point3 v = meshes[i].max();
|
||||
ret.x = std::max(ret.x, v.x);
|
||||
ret.y = std::max(ret.y, v.y);
|
||||
ret.z = std::max(ret.z, v.z);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
for(Mesh& m : meshes)
|
||||
m.clear();
|
||||
}
|
||||
|
||||
void offset(Point3 offset)
|
||||
{
|
||||
for(Mesh& m : meshes)
|
||||
for(MeshVertex& v : m.vertices)
|
||||
v.p += offset;
|
||||
}
|
||||
|
||||
void finalize()
|
||||
{
|
||||
// If a mesh position was given, put the mesh at this position in 3D space.
|
||||
if (hasSetting("mesh_position_x") || hasSetting("mesh_position_y") || hasSetting("mesh_position_z"))
|
||||
{
|
||||
Point3 object_min = min();
|
||||
Point3 object_max = max();
|
||||
Point3 object_size = object_max - object_min;
|
||||
Point3 object_offset = Point3(-object_min.x - object_size.x / 2, -object_min.y - object_size.y / 2, -object_min.z);
|
||||
if (hasSetting("mesh_position_x"))
|
||||
object_offset.x += getSettingInMicrons("mesh_position_x");
|
||||
if (hasSetting("mesh_position_y"))
|
||||
object_offset.y += getSettingInMicrons("mesh_position_y");
|
||||
if (hasSetting("mesh_position_z"))
|
||||
object_offset.z += getSettingInMicrons("mesh_position_z");
|
||||
offset(object_offset);
|
||||
}
|
||||
|
||||
//If the machine settings have been supplied, offset the given position vertices to the center of vertices (0,0,0) is at the bed center.
|
||||
if (hasSetting("machine_center_is_zero") && !getSettingBoolean("machine_center_is_zero"))
|
||||
{
|
||||
Point3 object_offset = Point3(0, 0, 0);
|
||||
if (hasSetting("machine_width"))
|
||||
object_offset.x = getSettingInMicrons("machine_width") / 2;
|
||||
if (hasSetting("machine_depth"))
|
||||
object_offset.y = getSettingInMicrons("machine_depth") / 2;
|
||||
offset(object_offset);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//A SimpleModel is a 3D model with 1 or more 3D volumes.
|
||||
class SimpleModel
|
||||
{
|
||||
public:
|
||||
vector<SimpleVolume> volumes;
|
||||
|
||||
Point3 min()
|
||||
{
|
||||
if (volumes.size() < 1)
|
||||
return Point3(0, 0, 0);
|
||||
Point3 ret = volumes[0].min();
|
||||
for(unsigned int i=0; i<volumes.size(); i++)
|
||||
{
|
||||
Point3 v = volumes[i].min();
|
||||
SET_MIN(ret.x, v.x);
|
||||
SET_MIN(ret.y, v.y);
|
||||
SET_MIN(ret.z, v.z);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
Point3 max()
|
||||
{
|
||||
if (volumes.size() < 1)
|
||||
return Point3(0, 0, 0);
|
||||
Point3 ret = volumes[0].max();
|
||||
for(unsigned int i=0; i<volumes.size(); i++)
|
||||
{
|
||||
Point3 v = volumes[i].max();
|
||||
SET_MAX(ret.x, v.x);
|
||||
SET_MAX(ret.y, v.y);
|
||||
SET_MAX(ret.z, v.z);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
SimpleModel* loadModelFromFile(SimpleModel*m,const char* filename, FMatrix3x3& matrix);
|
||||
bool loadMeshFromFile(PrintObject* object, const char* filename, FMatrix3x3& matrix);
|
||||
|
||||
#endif//MODELFILE_H
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
#include "multiVolumes.h"
|
||||
|
||||
namespace cura {
|
||||
|
||||
void carveMultipleVolumes(std::vector<SliceMeshStorage> &volumes)
|
||||
{
|
||||
//Go trough all the volumes, and remove the previous volume outlines from our own outline, so we never have overlapped areas.
|
||||
for(unsigned int idx=0; idx < volumes.size(); idx++)
|
||||
{
|
||||
for(unsigned int idx2=0; idx2<idx; idx2++)
|
||||
{
|
||||
for(unsigned int layerNr=0; layerNr < volumes[idx].layers.size(); layerNr++)
|
||||
{
|
||||
SliceLayer* layer1 = &volumes[idx].layers[layerNr];
|
||||
SliceLayer* layer2 = &volumes[idx2].layers[layerNr];
|
||||
for(unsigned int p1 = 0; p1 < layer1->parts.size(); p1++)
|
||||
{
|
||||
for(unsigned int p2 = 0; p2 < layer2->parts.size(); p2++)
|
||||
{
|
||||
layer1->parts[p1].outline = layer1->parts[p1].outline.difference(layer2->parts[p2].outline);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Expand each layer a bit and then keep the extra overlapping parts that overlap with other volumes.
|
||||
//This generates some overlap in dual extrusion, for better bonding in touching parts.
|
||||
void generateMultipleVolumesOverlap(std::vector<SliceMeshStorage> &volumes, int overlap)
|
||||
{
|
||||
if (volumes.size() < 2 || overlap <= 0) return;
|
||||
|
||||
for(unsigned int layerNr=0; layerNr < volumes[0].layers.size(); layerNr++)
|
||||
{
|
||||
Polygons fullLayer;
|
||||
for(unsigned int volIdx = 0; volIdx < volumes.size(); volIdx++)
|
||||
{
|
||||
SliceLayer* layer1 = &volumes[volIdx].layers[layerNr];
|
||||
for(unsigned int p1 = 0; p1 < layer1->parts.size(); p1++)
|
||||
{
|
||||
fullLayer = fullLayer.unionPolygons(layer1->parts[p1].outline.offset(20)); // TODO: put hard coded value in a variable with an explanatory name (and make var a parameter, and perhaps even a setting?)
|
||||
}
|
||||
}
|
||||
fullLayer = fullLayer.offset(-20); // TODO: put hard coded value in a variable with an explanatory name (and make var a parameter, and perhaps even a setting?)
|
||||
|
||||
for(unsigned int volIdx = 0; volIdx < volumes.size(); volIdx++)
|
||||
{
|
||||
SliceLayer* layer1 = &volumes[volIdx].layers[layerNr];
|
||||
for(unsigned int p1 = 0; p1 < layer1->parts.size(); p1++)
|
||||
{
|
||||
layer1->parts[p1].outline = fullLayer.intersection(layer1->parts[p1].outline.offset(overlap / 2));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}//namespace cura
|
||||
+4
-49
@@ -1,61 +1,16 @@
|
||||
#ifndef MULTIVOLUMES_H
|
||||
#define MULTIVOLUMES_H
|
||||
|
||||
#include "sliceDataStorage.h"
|
||||
|
||||
/* This file contains code to help fixing up and changing layers that are build from multiple volumes. */
|
||||
namespace cura {
|
||||
|
||||
void carveMultipleVolumes(vector<SliceVolumeStorage> &volumes)
|
||||
{
|
||||
//Go trough all the volumes, and remove the previous volume outlines from our own outline, so we never have overlapped areas.
|
||||
for(unsigned int idx=0; idx < volumes.size(); idx++)
|
||||
{
|
||||
for(unsigned int idx2=0; idx2<idx; idx2++)
|
||||
{
|
||||
for(unsigned int layerNr=0; layerNr < volumes[idx].layers.size(); layerNr++)
|
||||
{
|
||||
SliceLayer* layer1 = &volumes[idx].layers[layerNr];
|
||||
SliceLayer* layer2 = &volumes[idx2].layers[layerNr];
|
||||
for(unsigned int p1 = 0; p1 < layer1->parts.size(); p1++)
|
||||
{
|
||||
for(unsigned int p2 = 0; p2 < layer2->parts.size(); p2++)
|
||||
{
|
||||
layer1->parts[p1].outline = layer1->parts[p1].outline.difference(layer2->parts[p2].outline);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void carveMultipleVolumes(std::vector<SliceMeshStorage> &meshes);
|
||||
|
||||
//Expand each layer a bit and then keep the extra overlapping parts that overlap with other volumes.
|
||||
//This generates some overlap in dual extrusion, for better bonding in touching parts.
|
||||
void generateMultipleVolumesOverlap(vector<SliceVolumeStorage> &volumes, int overlap)
|
||||
{
|
||||
if (volumes.size() < 2 || overlap <= 0) return;
|
||||
|
||||
for(unsigned int layerNr=0; layerNr < volumes[0].layers.size(); layerNr++)
|
||||
{
|
||||
Polygons fullLayer;
|
||||
for(unsigned int volIdx = 0; volIdx < volumes.size(); volIdx++)
|
||||
{
|
||||
SliceLayer* layer1 = &volumes[volIdx].layers[layerNr];
|
||||
for(unsigned int p1 = 0; p1 < layer1->parts.size(); p1++)
|
||||
{
|
||||
fullLayer = fullLayer.unionPolygons(layer1->parts[p1].outline.offset(20));
|
||||
}
|
||||
}
|
||||
fullLayer = fullLayer.offset(-20);
|
||||
|
||||
for(unsigned int volIdx = 0; volIdx < volumes.size(); volIdx++)
|
||||
{
|
||||
SliceLayer* layer1 = &volumes[volIdx].layers[layerNr];
|
||||
for(unsigned int p1 = 0; p1 < layer1->parts.size(); p1++)
|
||||
{
|
||||
layer1->parts[p1].outline = fullLayer.intersection(layer1->parts[p1].outline.offset(overlap / 2));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void generateMultipleVolumesOverlap(std::vector<SliceMeshStorage> &meshes, int overlap);
|
||||
|
||||
}//namespace cura
|
||||
|
||||
|
||||
@@ -1,121 +0,0 @@
|
||||
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
|
||||
#include <stdio.h>
|
||||
|
||||
#include "utils/gettime.h"
|
||||
#include "utils/logoutput.h"
|
||||
#include "optimizedModel.h"
|
||||
|
||||
#define MELD_DIST MM2INT(0.03)
|
||||
OptimizedVolume::OptimizedVolume(SimpleVolume* volume, OptimizedModel* model)
|
||||
: model(model)
|
||||
{
|
||||
points.reserve(volume->faces.size() * 3);
|
||||
faces.reserve(volume->faces.size());
|
||||
|
||||
std::map<uint32_t, std::vector<uint32_t> > indexMap;
|
||||
|
||||
double t = getTime();
|
||||
for(uint32_t i=0; i<volume->faces.size(); i++)
|
||||
{
|
||||
OptimizedFace f;
|
||||
if((i%1000==0) && (getTime()-t)>2.0) cura::logProgress("optimized", i + 1, volume->faces.size());
|
||||
for(uint32_t j=0; j<3; j++)
|
||||
{
|
||||
Point3 p = volume->faces[i].v[j];
|
||||
int hash = ((p.x + MELD_DIST/2) / MELD_DIST) ^ (((p.y + MELD_DIST/2) / MELD_DIST) << 10) ^ (((p.z + MELD_DIST/2) / MELD_DIST) << 20);
|
||||
uint32_t idx;
|
||||
bool add = true;
|
||||
for(unsigned int n = 0; n < indexMap[hash].size(); n++)
|
||||
{
|
||||
if ((points[indexMap[hash][n]].p - p).testLength(MELD_DIST))
|
||||
{
|
||||
idx = indexMap[hash][n];
|
||||
add = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (add)
|
||||
{
|
||||
indexMap[hash].push_back(points.size());
|
||||
idx = points.size();
|
||||
points.push_back(p);
|
||||
}
|
||||
f.index[j] = idx;
|
||||
}
|
||||
if (f.index[0] != f.index[1] && f.index[0] != f.index[2] && f.index[1] != f.index[2])
|
||||
{
|
||||
//Check if there is a face with the same points
|
||||
bool duplicate = false;
|
||||
for(unsigned int _idx0 = 0; _idx0 < points[f.index[0]].faceIndexList.size(); _idx0++)
|
||||
{
|
||||
for(unsigned int _idx1 = 0; _idx1 < points[f.index[1]].faceIndexList.size(); _idx1++)
|
||||
{
|
||||
for(unsigned int _idx2 = 0; _idx2 < points[f.index[2]].faceIndexList.size(); _idx2++)
|
||||
{
|
||||
if (points[f.index[0]].faceIndexList[_idx0] == points[f.index[1]].faceIndexList[_idx1] && points[f.index[0]].faceIndexList[_idx0] == points[f.index[2]].faceIndexList[_idx2])
|
||||
duplicate = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!duplicate)
|
||||
{
|
||||
points[f.index[0]].faceIndexList.push_back(faces.size());
|
||||
points[f.index[1]].faceIndexList.push_back(faces.size());
|
||||
points[f.index[2]].faceIndexList.push_back(faces.size());
|
||||
faces.push_back(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
//fprintf(stdout, "\rAll faces are optimized in %5.1fs.\n",timeElapsed(t));
|
||||
|
||||
int openFacesCount = 0;
|
||||
for(unsigned int i=0;i<faces.size();i++)
|
||||
{
|
||||
OptimizedFace* f = &faces[i];
|
||||
f->touching[0] = getFaceIdxWithPoints(f->index[0], f->index[1], i);
|
||||
f->touching[1] = getFaceIdxWithPoints(f->index[1], f->index[2], i);
|
||||
f->touching[2] = getFaceIdxWithPoints(f->index[2], f->index[0], i);
|
||||
if (f->touching[0] == -1)
|
||||
openFacesCount++;
|
||||
if (f->touching[1] == -1)
|
||||
openFacesCount++;
|
||||
if (f->touching[2] == -1)
|
||||
openFacesCount++;
|
||||
}
|
||||
//fprintf(stdout, " Number of open faces: %i\n", openFacesCount);
|
||||
}
|
||||
|
||||
|
||||
void OptimizedModel::saveDebugSTL(const char* filename)
|
||||
{
|
||||
char buffer[80] = "Cura_Engine_STL_export";
|
||||
uint32_t n;
|
||||
uint16_t s;
|
||||
float flt;
|
||||
OptimizedVolume* vol = &volumes[0];
|
||||
FILE* f = fopen(filename, "wb");
|
||||
fwrite(buffer, 80, 1, f);
|
||||
n = vol->faces.size();
|
||||
fwrite(&n, sizeof(n), 1, f);
|
||||
for(unsigned int i=0;i<vol->faces.size();i++)
|
||||
{
|
||||
flt = 0;
|
||||
s = 0;
|
||||
fwrite(&flt, sizeof(flt), 1, f);
|
||||
fwrite(&flt, sizeof(flt), 1, f);
|
||||
fwrite(&flt, sizeof(flt), 1, f);
|
||||
|
||||
flt = INT2MM(vol->points[vol->faces[i].index[0]].p.x); fwrite(&flt, sizeof(flt), 1, f);
|
||||
flt = INT2MM(vol->points[vol->faces[i].index[0]].p.y); fwrite(&flt, sizeof(flt), 1, f);
|
||||
flt = INT2MM(vol->points[vol->faces[i].index[0]].p.z); fwrite(&flt, sizeof(flt), 1, f);
|
||||
flt = INT2MM(vol->points[vol->faces[i].index[1]].p.x); fwrite(&flt, sizeof(flt), 1, f);
|
||||
flt = INT2MM(vol->points[vol->faces[i].index[1]].p.y); fwrite(&flt, sizeof(flt), 1, f);
|
||||
flt = INT2MM(vol->points[vol->faces[i].index[1]].p.z); fwrite(&flt, sizeof(flt), 1, f);
|
||||
flt = INT2MM(vol->points[vol->faces[i].index[2]].p.x); fwrite(&flt, sizeof(flt), 1, f);
|
||||
flt = INT2MM(vol->points[vol->faces[i].index[2]].p.y); fwrite(&flt, sizeof(flt), 1, f);
|
||||
flt = INT2MM(vol->points[vol->faces[i].index[2]].p.z); fwrite(&flt, sizeof(flt), 1, f);
|
||||
|
||||
fwrite(&s, sizeof(s), 1, f);
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
|
||||
#ifndef OPTIMIZED_MODEL_H
|
||||
#define OPTIMIZED_MODEL_H
|
||||
|
||||
#include <map>
|
||||
#include "modelFile/modelFile.h"
|
||||
#include "settings.h"
|
||||
|
||||
class OptimizedFace
|
||||
{
|
||||
public:
|
||||
int index[3];
|
||||
int touching[3];
|
||||
};
|
||||
class OptimizedPoint3
|
||||
{
|
||||
public:
|
||||
Point3 p;
|
||||
vector<uint32_t> faceIndexList;
|
||||
|
||||
OptimizedPoint3(Point3 p): p(p) {}
|
||||
};
|
||||
|
||||
class OptimizedModel;
|
||||
class OptimizedVolume
|
||||
{
|
||||
public:
|
||||
OptimizedModel* model;
|
||||
vector<OptimizedPoint3> points;
|
||||
vector<OptimizedFace> faces;
|
||||
|
||||
OptimizedVolume(SimpleVolume* volume, OptimizedModel* model);
|
||||
|
||||
int getFaceIdxWithPoints(int idx0, int idx1, int notFaceIdx)
|
||||
{
|
||||
for(unsigned int i=0;i<points[idx0].faceIndexList.size();i++)
|
||||
{
|
||||
int f0 = points[idx0].faceIndexList[i];
|
||||
if (f0 == notFaceIdx) continue;
|
||||
for(unsigned int j=0;j<points[idx1].faceIndexList.size();j++)
|
||||
{
|
||||
int f1 = points[idx1].faceIndexList[j];
|
||||
if (f1 == notFaceIdx) continue;
|
||||
if (f0 == f1) return f0;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
};
|
||||
class OptimizedModel
|
||||
{
|
||||
public:
|
||||
vector<OptimizedVolume> volumes;
|
||||
Point3 modelSize;
|
||||
Point3 vMin, vMax;
|
||||
|
||||
OptimizedModel(SimpleModel* model, Point3 center)
|
||||
{
|
||||
for(unsigned int i=0; i<model->volumes.size(); i++)
|
||||
volumes.push_back(OptimizedVolume(&model->volumes[i], this));
|
||||
vMin = model->min();
|
||||
vMax = model->max();
|
||||
|
||||
Point3 vOffset((vMin.x + vMax.x) / 2, (vMin.y + vMax.y) / 2, vMin.z);
|
||||
if(ConfigSettings::config->autoCenter != 1)
|
||||
{
|
||||
vOffset.x = 0;
|
||||
vOffset.y = 0;
|
||||
if(ConfigSettings::config->autoCenter == 2)
|
||||
vOffset.z = 0;
|
||||
}
|
||||
vOffset -= center;
|
||||
for(unsigned int i=0; i<volumes.size(); i++)
|
||||
for(unsigned int n=0; n<volumes[i].points.size(); n++)
|
||||
volumes[i].points[n].p -= vOffset;
|
||||
|
||||
modelSize = vMax - vMin;
|
||||
vMin -= vOffset;
|
||||
vMax -= vOffset;
|
||||
}
|
||||
|
||||
void saveDebugSTL(const char* filename);
|
||||
};
|
||||
|
||||
#endif//OPTIMIZED_MODEL_H
|
||||
+201
-115
@@ -2,157 +2,243 @@
|
||||
#include <map>
|
||||
|
||||
#include "pathOrderOptimizer.h"
|
||||
#include "utils/logoutput.h"
|
||||
#include "utils/BucketGrid2D.h"
|
||||
|
||||
#define INLINE static inline
|
||||
|
||||
namespace cura {
|
||||
|
||||
static uint32_t hashPoint(const Point& p)
|
||||
{
|
||||
return (p.X / 20000) ^ (p.Y / 20000) << 8;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void PathOrderOptimizer::optimize()
|
||||
{
|
||||
const float incommingPerpundicularNormalScale = 0.0001f;
|
||||
bool picked[polygons.size()];
|
||||
memset(picked, false, sizeof(bool) * polygons.size());/// initialized as falses
|
||||
|
||||
std::map<uint32_t, std::vector<unsigned int>> location_to_polygon_map;
|
||||
std::vector<bool> picked;
|
||||
for(unsigned int i=0;i<polygons.size(); i++)
|
||||
for(unsigned int i_polygon=0 ; i_polygon<polygons.size() ; i_polygon++) /// find closest point to initial starting point within each polygon +initialize picked
|
||||
{
|
||||
int best = -1;
|
||||
float bestDist = 0xFFFFFFFFFFFFFFFFLL;
|
||||
PolygonRef poly = polygons[i];
|
||||
for(unsigned int j=0; j<poly.size(); j++)
|
||||
float bestDist = std::numeric_limits<float>::infinity();
|
||||
PolygonRef poly = polygons[i_polygon];
|
||||
for(unsigned int i_point=0; i_point<poly.size(); i_point++) /// get closest point in polygon
|
||||
{
|
||||
float dist = vSize2f(poly[j] - startPoint);
|
||||
float dist = vSize2f(poly[i_point] - startPoint);
|
||||
if (dist < bestDist)
|
||||
{
|
||||
best = j;
|
||||
best = i_point;
|
||||
bestDist = dist;
|
||||
}
|
||||
}
|
||||
polyStart.push_back(best);
|
||||
picked.push_back(false);
|
||||
|
||||
if (poly.size() == 2)
|
||||
{
|
||||
location_to_polygon_map[hashPoint(poly[0])].push_back(i);
|
||||
location_to_polygon_map[hashPoint(poly[1])].push_back(i);
|
||||
}
|
||||
//picked.push_back(false); /// initialize all picked values as false
|
||||
|
||||
assert(poly.size() != 2);
|
||||
}
|
||||
|
||||
Point incommingPerpundicularNormal(0, 0);
|
||||
Point p0 = startPoint;
|
||||
for(unsigned int n=0; n<polygons.size(); n++)
|
||||
|
||||
Point prev_point = startPoint;
|
||||
for(unsigned int i_polygon=0 ; i_polygon<polygons.size() ; i_polygon++) /// actual path order optimizer
|
||||
{
|
||||
int best = -1;
|
||||
float bestDist = 0xFFFFFFFFFFFFFFFFLL;
|
||||
|
||||
for(unsigned int i : location_to_polygon_map[hashPoint(p0)])
|
||||
float bestDist = std::numeric_limits<float>::infinity();
|
||||
|
||||
|
||||
for(unsigned int i_polygon=0 ; i_polygon<polygons.size() ; i_polygon++)
|
||||
{
|
||||
if (picked[i] || polygons[i].size() < 1)
|
||||
if (picked[i_polygon] || polygons[i_polygon].size() < 1) /// skip single-point-polygons
|
||||
continue;
|
||||
|
||||
float dist = vSize2f(polygons[i][0] - p0);
|
||||
dist += abs(dot(incommingPerpundicularNormal, normal(polygons[i][1] - polygons[i][0], 1000))) * incommingPerpundicularNormalScale;
|
||||
assert (polygons[i_polygon].size() != 2);
|
||||
|
||||
float dist = vSize2f(polygons[i_polygon][polyStart[i_polygon]] - prev_point);
|
||||
if (dist < bestDist)
|
||||
{
|
||||
best = i;
|
||||
best = i_polygon;
|
||||
bestDist = dist;
|
||||
polyStart[i] = 0;
|
||||
}
|
||||
dist = vSize2f(polygons[i][1] - p0);
|
||||
dist += abs(dot(incommingPerpundicularNormal, normal(polygons[i][0] - polygons[i][1], 1000))) * incommingPerpundicularNormalScale;
|
||||
if (dist < bestDist)
|
||||
{
|
||||
best = i;
|
||||
bestDist = dist;
|
||||
polyStart[i] = 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (best == -1)
|
||||
|
||||
|
||||
if (best > -1) /// should always be true; we should have been able to identify the best next polygon
|
||||
{
|
||||
for(unsigned int i=0;i<polygons.size(); i++)
|
||||
{
|
||||
if (picked[i] || polygons[i].size() < 1)
|
||||
continue;
|
||||
if (polygons[i].size() == 2)
|
||||
{
|
||||
float dist = vSize2f(polygons[i][0] - p0);
|
||||
dist += abs(dot(incommingPerpundicularNormal, normal(polygons[i][1] - polygons[i][0], 1000))) * incommingPerpundicularNormalScale;
|
||||
if (dist < bestDist)
|
||||
{
|
||||
best = i;
|
||||
bestDist = dist;
|
||||
polyStart[i] = 0;
|
||||
}
|
||||
dist = vSize2f(polygons[i][1] - p0);
|
||||
dist += abs(dot(incommingPerpundicularNormal, normal(polygons[i][0] - polygons[i][1], 1000))) * incommingPerpundicularNormalScale;
|
||||
if (dist < bestDist)
|
||||
{
|
||||
best = i;
|
||||
bestDist = dist;
|
||||
polyStart[i] = 1;
|
||||
}
|
||||
}else{
|
||||
float dist = vSize2f(polygons[i][polyStart[i]] - p0);
|
||||
if (dist < bestDist)
|
||||
{
|
||||
best = i;
|
||||
bestDist = dist;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (best > -1)
|
||||
{
|
||||
if (polygons[best].size() == 2)
|
||||
{
|
||||
int endIdx = (polyStart[best] + 1) % 2;
|
||||
p0 = polygons[best][endIdx];
|
||||
incommingPerpundicularNormal = crossZ(normal(polygons[best][endIdx] - polygons[best][polyStart[best]], 1000));
|
||||
}else{
|
||||
p0 = polygons[best][polyStart[best]];
|
||||
incommingPerpundicularNormal = Point(0, 0);
|
||||
}
|
||||
assert(polygons[best].size() != 2);
|
||||
|
||||
prev_point = polygons[best][polyStart[best]];
|
||||
|
||||
picked[best] = true;
|
||||
polyOrder.push_back(best);
|
||||
}
|
||||
else
|
||||
logError("Failed to find next closest polygon.\n");
|
||||
}
|
||||
|
||||
p0 = startPoint;
|
||||
for(int nr : polyOrder)
|
||||
|
||||
prev_point = startPoint;
|
||||
for(unsigned int n=0; n<polyOrder.size(); n++) /// decide final starting points in each polygon
|
||||
{
|
||||
PolygonRef poly = polygons[nr];
|
||||
if (poly.size() > 2)
|
||||
int i_polygon = polyOrder[n];
|
||||
int best = getClosestPointInPolygon(prev_point, i_polygon);
|
||||
polyStart[i_polygon] = best;
|
||||
prev_point = polygons[i_polygon][best];
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
inline int PathOrderOptimizer::getClosestPointInPolygon(Point prev_point, int i_polygon)
|
||||
{
|
||||
PolygonRef poly = polygons[i_polygon];
|
||||
int best = -1;
|
||||
float bestDist = std::numeric_limits<float>::infinity();
|
||||
bool orientation = poly.orientation();
|
||||
for(unsigned int i_point=0 ; i_point<poly.size() ; i_point++)
|
||||
{
|
||||
float dist = vSize2f(poly[i_point] - prev_point);
|
||||
Point n0 = normal(poly[(i_point-1+poly.size())%poly.size()] - poly[i_point], 2000);
|
||||
Point n1 = normal(poly[i_point] - poly[(i_point + 1) % poly.size()], 2000);
|
||||
float dot_score = dot(n0, n1) - dot(crossZ(n0), n1); /// prefer binnenbocht
|
||||
if (orientation)
|
||||
dot_score = -dot_score;
|
||||
if (dist + dot_score < bestDist)
|
||||
{
|
||||
int best = -1;
|
||||
float bestDist = 0xFFFFFFFFFFFFFFFFLL;
|
||||
bool orientation = poly.orientation();
|
||||
for(unsigned int i=0;i<poly.size(); i++)
|
||||
{
|
||||
const int64_t dot_score_scale = 2000;
|
||||
float dist = vSize2f(polygons[nr][i] - p0);
|
||||
Point n0 = normal(poly[(i+poly.size()-1)%poly.size()] - poly[i], dot_score_scale);
|
||||
Point n1 = normal(poly[i] - poly[(i + 1) % poly.size()], dot_score_scale);
|
||||
float dot_score = dot(n0, n1) - dot(crossZ(n0), n1);
|
||||
if (orientation)
|
||||
dot_score = -dot_score;
|
||||
dist += dot_score;
|
||||
if (dist < bestDist)
|
||||
{
|
||||
best = i;
|
||||
bestDist = dist;
|
||||
}
|
||||
}
|
||||
polyStart[nr] = best;
|
||||
best = i_point;
|
||||
bestDist = dist;
|
||||
}
|
||||
if (poly.size() <= 2)
|
||||
}
|
||||
return best;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void LineOrderOptimizer::optimize()
|
||||
{
|
||||
int gridSize = 5000; // the size of the cells in the hash grid.
|
||||
BucketGrid2D<unsigned int> line_bucket_grid(gridSize);
|
||||
bool picked[polygons.size()];
|
||||
memset(picked, false, sizeof(bool) * polygons.size());/// initialized as falses
|
||||
|
||||
for(unsigned int i_polygon=0 ; i_polygon<polygons.size() ; i_polygon++) /// find closest point to initial starting point within each polygon +initialize picked
|
||||
{
|
||||
int best = -1;
|
||||
float bestDist = std::numeric_limits<float>::infinity();
|
||||
PolygonRef poly = polygons[i_polygon];
|
||||
for(unsigned int i_point=0; i_point<poly.size(); i_point++) /// get closest point from polygon
|
||||
{
|
||||
p0 = poly[(polyStart[nr] + 1) % 2];
|
||||
}else{
|
||||
p0 = poly[polyStart[nr]];
|
||||
float dist = vSize2f(poly[i_point] - startPoint);
|
||||
if (dist < bestDist)
|
||||
{
|
||||
best = i_point;
|
||||
bestDist = dist;
|
||||
}
|
||||
}
|
||||
polyStart.push_back(best);
|
||||
|
||||
assert(poly.size() == 2);
|
||||
|
||||
line_bucket_grid.insert(poly[0], i_polygon);
|
||||
line_bucket_grid.insert(poly[1], i_polygon);
|
||||
|
||||
}
|
||||
|
||||
|
||||
Point incommingPerpundicularNormal(0, 0);
|
||||
Point prev_point = startPoint;
|
||||
for(unsigned int i_polygon=0 ; i_polygon<polygons.size() ; i_polygon++) /// actual path order optimizer
|
||||
{
|
||||
int best = -1;
|
||||
float bestDist = std::numeric_limits<float>::infinity();
|
||||
|
||||
for(unsigned int i_close_line_polygon : line_bucket_grid.findNearbyObjects(prev_point)) /// check if single-line-polygon is close to last point
|
||||
{
|
||||
if (picked[i_close_line_polygon] || polygons[i_close_line_polygon].size() < 1)
|
||||
continue;
|
||||
|
||||
|
||||
checkIfLineIsBest(i_close_line_polygon, best, bestDist, prev_point, incommingPerpundicularNormal);
|
||||
|
||||
}
|
||||
|
||||
if (best == -1) /// if single-line-polygon hasn't been found yet
|
||||
{
|
||||
for(unsigned int i_polygon=0 ; i_polygon<polygons.size() ; i_polygon++)
|
||||
{
|
||||
if (picked[i_polygon] || polygons[i_polygon].size() < 1) /// skip single-point-polygons
|
||||
continue;
|
||||
assert(polygons[i_polygon].size() == 2);
|
||||
|
||||
checkIfLineIsBest(i_polygon, best, bestDist, prev_point, incommingPerpundicularNormal);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (best > -1) /// should always be true; we should have been able to identify the best next polygon
|
||||
{
|
||||
assert(polygons[best].size() == 2);
|
||||
|
||||
int endIdx = polyStart[best] * -1 + 1; /// 1 -> 0 , 0 -> 1
|
||||
prev_point = polygons[best][endIdx];
|
||||
incommingPerpundicularNormal = crossZ(normal(polygons[best][endIdx] - polygons[best][polyStart[best]], 1000));
|
||||
|
||||
picked[best] = true;
|
||||
polyOrder.push_back(best);
|
||||
}
|
||||
else
|
||||
logError("Failed to find next closest line.\n");
|
||||
}
|
||||
|
||||
prev_point = startPoint;
|
||||
for(unsigned int n=0; n<polyOrder.size(); n++) /// decide final starting points in each polygon
|
||||
{
|
||||
int nr = polyOrder[n];
|
||||
PolygonRef poly = polygons[nr];
|
||||
int best = -1;
|
||||
float bestDist = std::numeric_limits<float>::infinity();
|
||||
bool orientation = poly.orientation();
|
||||
for(unsigned int i=0;i<poly.size(); i++)
|
||||
{
|
||||
float dist = vSize2f(polygons[nr][i] - prev_point);
|
||||
Point n0 = normal(poly[(i+poly.size()-1)%poly.size()] - poly[i], 2000);
|
||||
Point n1 = normal(poly[i] - poly[(i + 1) % poly.size()], 2000);
|
||||
float dot_score = dot(n0, n1) - dot(crossZ(n0), n1);
|
||||
if (orientation)
|
||||
dot_score = -dot_score;
|
||||
if (dist + dot_score < bestDist)
|
||||
{
|
||||
best = i;
|
||||
bestDist = dist + dot_score;
|
||||
}
|
||||
}
|
||||
|
||||
polyStart[nr] = best;
|
||||
assert(poly.size() == 2);
|
||||
prev_point = poly[best *-1 + 1]; /// 1 -> 0 , 0 -> 1
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
inline void LineOrderOptimizer::checkIfLineIsBest(unsigned int i_line_polygon, int& best, float& bestDist, Point& prev_point, Point& incommingPerpundicularNormal)
|
||||
{
|
||||
{ /// check distance to first point on line (0)
|
||||
float dist = vSize2f(polygons[i_line_polygon][0] - prev_point);
|
||||
dist += abs(dot(incommingPerpundicularNormal, normal(polygons[i_line_polygon][1] - polygons[i_line_polygon][0], 1000))) * 0.0001f; /// penalize sharp corners
|
||||
if (dist < bestDist)
|
||||
{
|
||||
best = i_line_polygon;
|
||||
bestDist = dist;
|
||||
polyStart[i_line_polygon] = 0;
|
||||
}
|
||||
}
|
||||
{ /// check distance to second point on line (1)
|
||||
float dist = vSize2f(polygons[i_line_polygon][1] - prev_point);
|
||||
dist += abs(dot(incommingPerpundicularNormal, normal(polygons[i_line_polygon][0] - polygons[i_line_polygon][1], 1000) )) * 0.0001f; /// penalize sharp corners
|
||||
if (dist < bestDist)
|
||||
{
|
||||
best = i_line_polygon;
|
||||
bestDist = dist;
|
||||
polyStart[i_line_polygon] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,13 +7,18 @@
|
||||
|
||||
namespace cura {
|
||||
|
||||
//! Parts order optimization class.
|
||||
/*!
|
||||
* Utility class for optimizing the path order by minimizing the distance traveled between printing different parts in the layer.
|
||||
* The order of polygons is optimized and the startingpoint within each polygon is chosen.
|
||||
*/
|
||||
class PathOrderOptimizer
|
||||
{
|
||||
public:
|
||||
Point startPoint;
|
||||
vector<PolygonRef> polygons;
|
||||
vector<int> polyStart;
|
||||
vector<int> polyOrder;
|
||||
Point startPoint; //!< The location of the nozzle before starting to print the current layer
|
||||
std::vector<PolygonRef> polygons; //!< the parts of the layer (in arbitrary order)
|
||||
std::vector<int> polyStart; //!< polygons[i][polyStart[i]] = point of polygon i which is to be the starting point in printing the polygon
|
||||
std::vector<int> polyOrder; //!< the optimized order as indices in #polygons
|
||||
|
||||
PathOrderOptimizer(Point startPoint)
|
||||
{
|
||||
@@ -24,14 +29,53 @@ public:
|
||||
{
|
||||
this->polygons.push_back(polygon);
|
||||
}
|
||||
|
||||
|
||||
void addPolygons(Polygons& polygons)
|
||||
{
|
||||
for(unsigned int i=0;i<polygons.size(); i++)
|
||||
this->polygons.push_back(polygons[i]);
|
||||
}
|
||||
|
||||
void optimize();
|
||||
|
||||
void optimize(); //!< sets #polyStart and #polyOrder
|
||||
|
||||
private:
|
||||
int getClosestPointInPolygon(Point prev, int i_polygon); //!< returns the index of the closest point
|
||||
|
||||
|
||||
};
|
||||
//! Line path order optimization class.
|
||||
/*!
|
||||
* Utility class for optimizing the path order by minimizing the distance traveled between printing different lines within a part.
|
||||
*/
|
||||
class LineOrderOptimizer
|
||||
{
|
||||
public:
|
||||
Point startPoint; //!< The location of the nozzle before starting to print the current layer
|
||||
std::vector<PolygonRef> polygons; //!< the parts of the layer (in arbitrary order)
|
||||
std::vector<int> polyStart; //!< polygons[i][polyStart[i]] = point of polygon i which is to be the starting point in printing the polygon
|
||||
std::vector<int> polyOrder; //!< the optimized order as indices in #polygons
|
||||
|
||||
LineOrderOptimizer(Point startPoint)
|
||||
{
|
||||
this->startPoint = startPoint;
|
||||
}
|
||||
|
||||
void addPolygon(PolygonRef polygon)
|
||||
{
|
||||
this->polygons.push_back(polygon);
|
||||
}
|
||||
|
||||
void addPolygons(Polygons& polygons)
|
||||
{
|
||||
for(unsigned int i=0;i<polygons.size(); i++)
|
||||
this->polygons.push_back(polygons[i]);
|
||||
}
|
||||
|
||||
void optimize(); //!< sets #polyStart and #polyOrder
|
||||
|
||||
private:
|
||||
void checkIfLineIsBest(unsigned int i_line_polygon, int& best, float& bestDist, Point& prev_point, Point& incommingPerpundicularNormal);
|
||||
|
||||
};
|
||||
|
||||
}//namespace cura
|
||||
|
||||
+9
-9
@@ -6,18 +6,18 @@ namespace cura {
|
||||
|
||||
void generateRaft(SliceDataStorage& storage, int distance)
|
||||
{
|
||||
for(unsigned int volumeIdx = 0; volumeIdx < storage.volumes.size(); volumeIdx++)
|
||||
for(SliceMeshStorage& mesh : storage.meshes)
|
||||
{
|
||||
if (storage.volumes[volumeIdx].layers.size() < 1) continue;
|
||||
SliceLayer* layer = &storage.volumes[volumeIdx].layers[0];
|
||||
for(unsigned int i=0; i<layer->parts.size(); i++)
|
||||
{
|
||||
storage.raftOutline = storage.raftOutline.unionPolygons(layer->parts[i].outline.offset(distance));
|
||||
}
|
||||
if (mesh.layers.size() < 1) continue;
|
||||
SliceLayer* layer = &mesh.layers[0];
|
||||
for(SliceLayerPart& part : layer->parts)
|
||||
storage.raftOutline = storage.raftOutline.unionPolygons(part.outline.offset(distance));
|
||||
}
|
||||
|
||||
SupportPolyGenerator supportGenerator(storage.support, 0);
|
||||
storage.raftOutline = storage.raftOutline.unionPolygons(supportGenerator.polygons.offset(distance));
|
||||
Polygons support;
|
||||
if (storage.support.generated)
|
||||
support = storage.support.supportAreasPerLayer[0];
|
||||
storage.raftOutline = storage.raftOutline.unionPolygons(support.offset(distance));
|
||||
storage.raftOutline = storage.raftOutline.unionPolygons(storage.wipeTower.offset(distance));
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,214 @@
|
||||
#include "settingRegistry.h"
|
||||
|
||||
#include <sstream>
|
||||
#include "utils/logoutput.h"
|
||||
|
||||
#include "rapidjson/rapidjson.h"
|
||||
#include "rapidjson/document.h"
|
||||
#include "rapidjson/error/en.h"
|
||||
#include "rapidjson/filereadstream.h"
|
||||
|
||||
SettingRegistry SettingRegistry::instance; // define settingRegistry
|
||||
|
||||
bool SettingRegistry::settingExists(std::string key) const
|
||||
{
|
||||
return settings.find(key) != settings.end();
|
||||
}
|
||||
|
||||
const SettingConfig* SettingRegistry::getSettingConfig(std::string key)
|
||||
{
|
||||
if (settings.find(key) == settings.end())
|
||||
return nullptr;
|
||||
return settings[key];
|
||||
}
|
||||
|
||||
SettingRegistry::SettingRegistry()
|
||||
{
|
||||
}
|
||||
|
||||
bool SettingRegistry::settingsLoaded()
|
||||
{
|
||||
return settings.size() > 0;
|
||||
}
|
||||
|
||||
bool SettingRegistry::loadJSON(std::string filename)
|
||||
{
|
||||
rapidjson::Document json_document;
|
||||
|
||||
{
|
||||
FILE* f = fopen(filename.c_str(), "rb");
|
||||
if (!f)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
char read_buffer[4096];
|
||||
rapidjson::FileReadStream reader_stream(f, read_buffer, sizeof(read_buffer));
|
||||
json_document.ParseStream(reader_stream);
|
||||
fclose(f);
|
||||
if (json_document.HasParseError())
|
||||
{
|
||||
cura::logError("Error(offset %u): %s\n", (unsigned)json_document.GetErrorOffset(), GetParseError_En(json_document.GetParseError()));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!json_document.IsObject())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!json_document.HasMember("categories"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
categories.emplace_back("machine_settings", "Machine Settings");
|
||||
SettingCategory* category_machine_settings = &categories.back();
|
||||
_addSettingsToCategory(category_machine_settings, json_document["machine_settings"], NULL);
|
||||
|
||||
categories.emplace_back("mesh_settings", "TEMPORARY");
|
||||
SettingCategory* category_mesh_settings = &categories.back();
|
||||
{
|
||||
SettingConfig* config = category_mesh_settings->addChild("mesh_position_x", "mesh_position_x");
|
||||
config->setDefault("0");
|
||||
if (settingExists(config->getKey()))
|
||||
{
|
||||
cura::logError("Duplicate definition of setting: %s\n", config->getKey().c_str());
|
||||
}
|
||||
settings[config->getKey()] = config;
|
||||
}
|
||||
{
|
||||
SettingConfig* config = category_mesh_settings->addChild("mesh_position_y", "mesh_position_y");
|
||||
config->setDefault("0");
|
||||
if (settingExists(config->getKey()))
|
||||
{
|
||||
cura::logError("Duplicate definition of setting: %s\n", config->getKey().c_str());
|
||||
}
|
||||
settings[config->getKey()] = config;
|
||||
}
|
||||
{
|
||||
SettingConfig* config = category_mesh_settings->addChild("mesh_position_z", "mesh_position_z");
|
||||
config->setDefault("0");
|
||||
if (settingExists(config->getKey()))
|
||||
{
|
||||
cura::logError("Duplicate definition of setting: %s\n", config->getKey().c_str());
|
||||
}
|
||||
settings[config->getKey()] = config;
|
||||
}
|
||||
|
||||
|
||||
for (rapidjson::Value::ConstMemberIterator category_iterator = json_document["categories"].MemberBegin(); category_iterator != json_document["categories"].MemberEnd(); ++category_iterator)
|
||||
{
|
||||
if (!category_iterator->value.IsObject())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (!category_iterator->value.HasMember("label") || !category_iterator->value["label"].IsString())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (!category_iterator->value.HasMember("settings") || !category_iterator->value["settings"].IsObject())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
categories.emplace_back(category_iterator->name.GetString(), category_iterator->value["label"].GetString());
|
||||
SettingCategory* category = &categories.back();
|
||||
|
||||
_addSettingsToCategory(category, category_iterator->value["settings"], NULL);
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SettingRegistry::_addSettingsToCategory(SettingCategory* category, const rapidjson::Value& json_object, SettingConfig* parent)
|
||||
{
|
||||
for (rapidjson::Value::ConstMemberIterator setting_iterator = json_object.MemberBegin(); setting_iterator != json_object.MemberEnd(); ++setting_iterator)
|
||||
{
|
||||
const rapidjson::Value& data = setting_iterator->value;
|
||||
|
||||
std::string label;
|
||||
if (!setting_iterator->value.HasMember("label") || !data["label"].IsString())
|
||||
{
|
||||
label = "N/A";
|
||||
}
|
||||
else
|
||||
{
|
||||
label = data["label"].GetString();
|
||||
}
|
||||
|
||||
/// Create the new setting config object.
|
||||
SettingConfig* config;
|
||||
if (parent)
|
||||
config = parent->addChild(setting_iterator->name.GetString(), label);
|
||||
else
|
||||
config = category->addChild(setting_iterator->name.GetString(), label);
|
||||
|
||||
|
||||
/// Fill the setting config object with data we have in the json file.
|
||||
if (data.HasMember("type") && data["type"].IsString())
|
||||
{
|
||||
config->setType(data["type"].GetString());
|
||||
}
|
||||
if (data.HasMember("default"))
|
||||
{
|
||||
if (data["default"].IsString())
|
||||
{
|
||||
config->setDefault(data["default"].GetString());
|
||||
}
|
||||
else if (data["default"].IsTrue())
|
||||
{
|
||||
config->setDefault("true");
|
||||
}
|
||||
else if (data["default"].IsFalse())
|
||||
{
|
||||
config->setDefault("false");
|
||||
}
|
||||
else if (data["default"].IsNumber())
|
||||
{
|
||||
std::ostringstream ss;
|
||||
ss << data["default"].GetDouble();
|
||||
config->setDefault(ss.str());
|
||||
}
|
||||
}
|
||||
if (data.HasMember("unit") && data["unit"].IsString())
|
||||
{
|
||||
config->setUnit(data["unit"].GetString());
|
||||
}
|
||||
|
||||
/// Register the setting in the settings map lookup.
|
||||
if (settingExists(config->getKey()))
|
||||
{
|
||||
cura::logError("Duplicate definition of setting: %s\n", config->getKey().c_str());
|
||||
}
|
||||
settings[config->getKey()] = config;
|
||||
|
||||
/// When this setting has children, add those children to this setting.
|
||||
if (data.HasMember("children") && data["children"].IsObject())
|
||||
{
|
||||
_addSettingsToCategory(category, data["children"], config);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SettingCategory::SettingCategory(std::string key, std::string label)
|
||||
: label(label), key(key)
|
||||
{
|
||||
}
|
||||
|
||||
SettingConfig* SettingCategory::addChild(std::string key, std::string label)
|
||||
{
|
||||
children.emplace_back(key, label, nullptr);
|
||||
return &children.back();
|
||||
}
|
||||
|
||||
SettingConfig::SettingConfig(std::string key, std::string label, SettingConfig* parent)
|
||||
: label(label), key(key), parent(parent)
|
||||
{
|
||||
}
|
||||
|
||||
SettingConfig* SettingConfig::addChild(std::string key, std::string label)
|
||||
{
|
||||
children.emplace_back(key, label, this);
|
||||
return &children.back();
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
#ifndef SETTING_REGISTRY_H
|
||||
#define SETTING_REGISTRY_H
|
||||
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "rapidjson/document.h"
|
||||
|
||||
// Forward declaration
|
||||
class SettingConfig;
|
||||
|
||||
/*!
|
||||
* Setting category.
|
||||
* Filled from the fdmprinter.json file. Contains one or more children settings.
|
||||
*/
|
||||
class SettingCategory
|
||||
{
|
||||
private:
|
||||
std::string label;
|
||||
std::string key;
|
||||
std::list<SettingConfig> children;
|
||||
public:
|
||||
SettingCategory(std::string key, std::string label);
|
||||
|
||||
SettingConfig* addChild(std::string key, std::string label);
|
||||
};
|
||||
|
||||
/*!
|
||||
* Single setting data.
|
||||
* Filled from the fdmprinter.json file. Can contain child settings, and is registered in the
|
||||
* setting registry with it's key.
|
||||
*/
|
||||
class SettingConfig
|
||||
{
|
||||
private:
|
||||
std::string label;
|
||||
std::string key;
|
||||
std::string type;
|
||||
std::string default_value;
|
||||
std::string unit;
|
||||
SettingConfig* parent;
|
||||
std::list<SettingConfig> children;
|
||||
public:
|
||||
SettingConfig(std::string key, std::string label, SettingConfig* parent);
|
||||
|
||||
SettingConfig* addChild(std::string key, std::string label);
|
||||
|
||||
std::string getKey() const
|
||||
{
|
||||
return key;
|
||||
}
|
||||
|
||||
void setType(std::string type)
|
||||
{
|
||||
this->type = type;
|
||||
}
|
||||
|
||||
std::string getType() const
|
||||
{
|
||||
return type;
|
||||
}
|
||||
|
||||
void setDefault(std::string default_value)
|
||||
{
|
||||
this->default_value = default_value;
|
||||
}
|
||||
|
||||
std::string getDefaultValue() const
|
||||
{
|
||||
return default_value;
|
||||
}
|
||||
|
||||
void setUnit(std::string unit)
|
||||
{
|
||||
this->unit = unit;
|
||||
}
|
||||
|
||||
std::string getUnit() const
|
||||
{
|
||||
return unit;
|
||||
}
|
||||
};
|
||||
|
||||
/*!
|
||||
* Setting registry.
|
||||
* There is a single global setting registry.
|
||||
* This registry contains all known setting keys.
|
||||
* The registry also contains the settings categories to build up the setting hiarcy from the json file.
|
||||
*/
|
||||
class SettingRegistry
|
||||
{
|
||||
private:
|
||||
static SettingRegistry instance;
|
||||
|
||||
std::map<std::string, SettingConfig*> settings;
|
||||
std::list<SettingCategory> categories;
|
||||
public:
|
||||
static SettingRegistry* getInstance() { return &instance; }
|
||||
|
||||
bool settingExists(std::string key) const;
|
||||
const SettingConfig* getSettingConfig(std::string key);
|
||||
|
||||
bool settingsLoaded();
|
||||
bool loadJSON(std::string filename);
|
||||
private:
|
||||
SettingRegistry();
|
||||
|
||||
void _addSettingsToCategory(SettingCategory* category, const rapidjson::Value& json_object, SettingConfig* parent);
|
||||
};
|
||||
|
||||
#endif//SETTING_REGISTRY_H
|
||||
+163
-250
@@ -1,275 +1,188 @@
|
||||
#include <cctype>
|
||||
#include <fstream>
|
||||
#include <stdio.h>
|
||||
#include <sstream> // ostringstream
|
||||
#include "utils/logoutput.h"
|
||||
#include "utils/string.h"
|
||||
|
||||
#include "settings.h"
|
||||
#include "settingRegistry.h"
|
||||
|
||||
#define LTRIM_STRING(s) do { while(((s).length() > 0) && isspace((s)[0])) { (s).erase(0, 1); } } while(0)
|
||||
#define RTRIM_STRING(s) do { while(((s).length() > 0) && isspace((s)[(s).length() - 1])) { (s).erase((s).length() - 1); } } while(0)
|
||||
#define TRIM_STRING(s) do { LTRIM_STRING(s); RTRIM_STRING(s); } while(0)
|
||||
#define STRINGIFY(_s) #_s
|
||||
#define SETTING(name, default) do { _index.push_back(_ConfigSettingIndex(STRINGIFY(name), &name)); name = (default); } while(0)
|
||||
#define SETTING2(name, altname, default) do { _index.push_back(_ConfigSettingIndex(STRINGIFY(name), &name)); _index.push_back(_ConfigSettingIndex(STRINGIFY(altname), &name)); name = (default); } while(0)
|
||||
//c++11 no longer defines M_PI, so add our own constant.
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.14159265358979323846
|
||||
#endif
|
||||
|
||||
ConfigSettings *ConfigSettings::config = NULL;
|
||||
|
||||
ConfigSettings::ConfigSettings()
|
||||
SettingsBase::SettingsBase()
|
||||
: parent(NULL)
|
||||
{
|
||||
config = this;
|
||||
SETTING(layerThickness, 100);
|
||||
SETTING(initialLayerThickness, 300);
|
||||
SETTING(filamentDiameter, 2890);
|
||||
SETTING(filamentFlow, 100);
|
||||
SETTING(layer0extrusionWidth, 600);
|
||||
SETTING(extrusionWidth, 400);
|
||||
SETTING(insetCount, 2);
|
||||
SETTING(downSkinCount, 6);
|
||||
SETTING(upSkinCount, 6);
|
||||
SETTING(skirtDistance, 6000);
|
||||
SETTING(skirtLineCount, 1);
|
||||
SETTING(skirtMinLength, 0);
|
||||
|
||||
SETTING(initialSpeedupLayers, 4);
|
||||
SETTING(initialLayerSpeed, 20);
|
||||
SETTING(printSpeed, 50);
|
||||
SETTING(inset0Speed, 50);
|
||||
SETTING(insetXSpeed, 50);
|
||||
SETTING(moveSpeed, 150);
|
||||
SETTING(fanFullOnLayerNr, 2);
|
||||
|
||||
SETTING(sparseInfillLineDistance, 100 * extrusionWidth / 20);
|
||||
SETTING(infillOverlap, 15);
|
||||
SETTING(infillSpeed, 50);
|
||||
SETTING(infillPattern, INFILL_AUTOMATIC);
|
||||
|
||||
SETTING(supportType, SUPPORT_TYPE_GRID);
|
||||
SETTING(supportAngle, -1);
|
||||
SETTING(supportEverywhere, 0);
|
||||
SETTING(supportLineDistance, sparseInfillLineDistance);
|
||||
SETTING(supportXYDistance, 700);
|
||||
SETTING(supportZDistance, 150);
|
||||
SETTING(supportExtruder, -1);
|
||||
|
||||
SETTING(retractionAmount, 4500);
|
||||
SETTING(retractionAmountPrime, 0);
|
||||
SETTING(retractionSpeed, 45);
|
||||
SETTING(retractionAmountExtruderSwitch, 14500);
|
||||
SETTING(retractionMinimalDistance, 1500);
|
||||
SETTING(minimalExtrusionBeforeRetraction, 100);
|
||||
SETTING(retractionZHop, 0);
|
||||
|
||||
SETTING(enableCombing, 1);
|
||||
SETTING(enableOozeShield, 0);
|
||||
SETTING(wipeTowerSize, 0);
|
||||
SETTING(multiVolumeOverlap, 0);
|
||||
SETTING2(objectPosition.X, posx, 102500);
|
||||
SETTING2(objectPosition.Y, posy, 102500);
|
||||
SETTING(objectSink, 0);
|
||||
SETTING(autoCenter, 1);
|
||||
|
||||
SETTING(raftMargin, 5000);
|
||||
SETTING(raftLineSpacing, 1000);
|
||||
SETTING(raftBaseThickness, 0);
|
||||
SETTING(raftBaseLinewidth, 0);
|
||||
SETTING(raftInterfaceThickness, 0);
|
||||
SETTING(raftInterfaceLinewidth, 0);
|
||||
SETTING(raftInterfaceLineSpacing, 0);
|
||||
SETTING(raftAirGap, 0);
|
||||
SETTING(raftAirGapLayer0, 0);
|
||||
SETTING(raftBaseSpeed, 0);
|
||||
SETTING(raftFanSpeed, 0);
|
||||
SETTING(raftSurfaceThickness, 0);
|
||||
SETTING(raftSurfaceLinewidth, 0);
|
||||
SETTING(raftSurfaceLineSpacing, 0);
|
||||
SETTING(raftSurfaceLayers, 0);
|
||||
SETTING(raftSurfaceSpeed, 0);
|
||||
|
||||
SETTING(minimalLayerTime, 5);
|
||||
SETTING(minimalFeedrate, 10);
|
||||
SETTING(coolHeadLift, 0);
|
||||
SETTING(fanSpeedMin, 100);
|
||||
SETTING(fanSpeedMax, 100);
|
||||
|
||||
SETTING(fixHorrible, 0);
|
||||
SETTING(spiralizeMode, 0);
|
||||
SETTING(simpleMode, 0);
|
||||
SETTING(gcodeFlavor, GCODE_FLAVOR_REPRAP);
|
||||
|
||||
memset(extruderOffset, 0, sizeof(extruderOffset));
|
||||
SETTING(extruderOffset[0].X, 0); // No one says that extruder 0 can not have an offset!
|
||||
SETTING(extruderOffset[0].Y, 0);
|
||||
SETTING(extruderOffset[1].X, 0);
|
||||
SETTING(extruderOffset[1].Y, 0);
|
||||
SETTING(extruderOffset[2].X, 0);
|
||||
SETTING(extruderOffset[2].Y, 0);
|
||||
SETTING(extruderOffset[3].X, 0);
|
||||
SETTING(extruderOffset[3].Y, 0);
|
||||
SETTING(extruderOffset[4].X, 0);
|
||||
SETTING(extruderOffset[4].Y, 0);
|
||||
SETTING(extruderOffset[5].X, 0);
|
||||
SETTING(extruderOffset[5].Y, 0);
|
||||
SETTING(extruderOffset[6].X, 0);
|
||||
SETTING(extruderOffset[6].Y, 0);
|
||||
SETTING(extruderOffset[7].X, 0);
|
||||
SETTING(extruderOffset[7].Y, 0);
|
||||
SETTING(extruderOffset[8].X, 0);
|
||||
SETTING(extruderOffset[8].Y, 0);
|
||||
SETTING(extruderOffset[9].X, 0);
|
||||
SETTING(extruderOffset[9].Y, 0);
|
||||
SETTING(extruderOffset[10].X, 0);
|
||||
SETTING(extruderOffset[10].Y, 0);
|
||||
SETTING(extruderOffset[11].X, 0);
|
||||
SETTING(extruderOffset[11].Y, 0);
|
||||
SETTING(extruderOffset[12].X, 0);
|
||||
SETTING(extruderOffset[12].Y, 0);
|
||||
SETTING(extruderOffset[13].X, 0);
|
||||
SETTING(extruderOffset[13].Y, 0);
|
||||
SETTING(extruderOffset[14].X, 0);
|
||||
SETTING(extruderOffset[14].Y, 0);
|
||||
SETTING(extruderOffset[15].X, 0);
|
||||
SETTING(extruderOffset[15].Y, 0);
|
||||
|
||||
startCode =
|
||||
"M109 S210 ;Heatup to 210C\n"
|
||||
"G21 ;metric values\n"
|
||||
"G90 ;absolute positioning\n"
|
||||
"G28 ;Home\n"
|
||||
"G1 Z15.0 F300 ;move the platform down 15mm\n"
|
||||
"G92 E0 ;zero the extruded length\n"
|
||||
"G1 F200 E5 ;extrude 5mm of feed stock\n"
|
||||
"G92 E0 ;zero the extruded length again\n";
|
||||
endCode =
|
||||
"M104 S0 ;extruder heater off\n"
|
||||
"M140 S0 ;heated bed heater off (if you have it)\n"
|
||||
"G91 ;relative positioning\n"
|
||||
"G1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\n"
|
||||
"G1 Z+0.5 E-5 X-20 Y-20 F9000 ;move Z up a bit and retract filament even more\n"
|
||||
"G28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\n"
|
||||
"M84 ;steppers off\n"
|
||||
"G90 ;absolute positioning\n";
|
||||
}
|
||||
|
||||
#undef STRINGIFY
|
||||
#undef SETTING
|
||||
|
||||
bool ConfigSettings::setSetting(const char* key, const char* value)
|
||||
SettingsBase::SettingsBase(SettingsBase* parent)
|
||||
: parent(parent)
|
||||
{
|
||||
for(unsigned int n=0; n < _index.size(); n++)
|
||||
}
|
||||
|
||||
void SettingsBase::setSetting(std::string key, std::string value)
|
||||
{
|
||||
if (SettingRegistry::getInstance()->settingExists(key))
|
||||
{
|
||||
if (stringcasecompare(key, _index[n].key) == 0)
|
||||
{
|
||||
*_index[n].ptr = atoi(value);
|
||||
return true;
|
||||
}
|
||||
setting_values[key] = value;
|
||||
}
|
||||
if (stringcasecompare(key, "startCode") == 0)
|
||||
else
|
||||
{
|
||||
cura::logError("Warning: setting an unregistered setting %s\n", key.c_str() );
|
||||
setting_values[key] = value; // Handy when programmers are in the process of introducing a new setting
|
||||
}
|
||||
}
|
||||
|
||||
std::string SettingsBase::getSettingString(std::string key)
|
||||
{
|
||||
if (setting_values.find(key) != setting_values.end())
|
||||
{
|
||||
return setting_values[key];
|
||||
}
|
||||
if (parent)
|
||||
{
|
||||
return parent->getSettingString(key);
|
||||
}
|
||||
|
||||
if (SettingRegistry::getInstance()->settingExists(key))
|
||||
{
|
||||
setting_values[key] = SettingRegistry::getInstance()->getSettingConfig(key)->getDefaultValue();
|
||||
cura::logError("Using default for: %s = %s\n", key.c_str(), setting_values[key].c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
setting_values[key] = "";
|
||||
cura::logError("Unregistered setting %s\n", key.c_str());
|
||||
}
|
||||
return setting_values[key];
|
||||
}
|
||||
|
||||
bool SettingsBase::hasSetting(std::string key)
|
||||
{
|
||||
if (setting_values.find(key) != setting_values.end())
|
||||
{
|
||||
this->startCode = value;
|
||||
return true;
|
||||
}
|
||||
if (stringcasecompare(key, "endCode") == 0)
|
||||
if (parent)
|
||||
{
|
||||
this->endCode = value;
|
||||
return true;
|
||||
}
|
||||
if (stringcasecompare(key, "preSwitchExtruderCode") == 0)
|
||||
{
|
||||
this->preSwitchExtruderCode = value;
|
||||
return true;
|
||||
}
|
||||
if (stringcasecompare(key, "postSwitchExtruderCode") == 0)
|
||||
{
|
||||
this->postSwitchExtruderCode = value;
|
||||
return true;
|
||||
return parent->hasSetting(key);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ConfigSettings::readSettings(void) {
|
||||
return readSettings(DEFAULT_CONFIG_PATH);
|
||||
int SettingsBase::getSettingAsIndex(std::string key)
|
||||
{
|
||||
std::string value = getSettingString(key);
|
||||
return atoi(value.c_str());
|
||||
}
|
||||
|
||||
bool ConfigSettings::readSettings(const char* path) {
|
||||
std::ifstream config(path);
|
||||
std::string line;
|
||||
size_t line_number = 0;
|
||||
|
||||
if(!config.good()) return false;
|
||||
|
||||
while(config.good()) {
|
||||
bool multilineContent = false;
|
||||
size_t pos = std::string::npos;
|
||||
std::getline(config, line);
|
||||
line_number += 1;
|
||||
|
||||
// De-comment and trim, skipping anything that shows up empty
|
||||
pos = line.find_first_of('#');
|
||||
if(pos != std::string::npos) line.erase(pos);
|
||||
TRIM_STRING(line);
|
||||
if(line.length() == 0) continue;
|
||||
|
||||
// Split into key = val
|
||||
std::string key(""), val("");
|
||||
pos = line.find_first_of('=');
|
||||
if(pos != std::string::npos && line.length() > (pos + 1)) {
|
||||
key = line.substr(0, pos);
|
||||
val = line.substr(pos + 1);
|
||||
TRIM_STRING(key);
|
||||
TRIM_STRING(val);
|
||||
}
|
||||
|
||||
// Are we about to read a multiline string?
|
||||
if(val == CONFIG_MULTILINE_SEPARATOR) {
|
||||
val = "";
|
||||
multilineContent = true;
|
||||
bool done_multiline = false;
|
||||
|
||||
while(config.good() && !done_multiline) {
|
||||
std::getline(config, line);
|
||||
line_number += 1;
|
||||
|
||||
// We RTRIM the line for two reasons:
|
||||
//
|
||||
// 1) Make sure that a direct == comparison with '"""' works without
|
||||
// worrying about trailing space.
|
||||
// 2) Nobody likes trailing whitespace anyway
|
||||
RTRIM_STRING(line);
|
||||
|
||||
// Either accumuliate or terminate
|
||||
if(line == CONFIG_MULTILINE_SEPARATOR) {
|
||||
done_multiline = true;
|
||||
// Make sure we don't add an extra trailing newline
|
||||
// to the parsed value
|
||||
RTRIM_STRING(val);
|
||||
}
|
||||
else {
|
||||
line += "\n";
|
||||
val += line;
|
||||
}
|
||||
}
|
||||
|
||||
// If we drop out but didn't finish reading, something failed
|
||||
if(!done_multiline) {
|
||||
cura::logError("Config(%s):L%zd: Failed while reading multiline string.\n", path, line_number);
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Fail if we don't get a key and val
|
||||
if(key.length() == 0 || (val.length() == 0 && !multilineContent)) {
|
||||
cura::logError("Config(%s): Line %zd: No key value pair found\n", path, line_number);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set a config setting for the current K=V
|
||||
if(!setSetting(key.c_str(), val.c_str())) {
|
||||
cura::logError("Config(%s):L%zd: Failed to set '%s' to '%s'\n", path, line_number, key.c_str(), val.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
int SettingsBase::getSettingAsCount(std::string key)
|
||||
{
|
||||
std::string value = getSettingString(key);
|
||||
return atoi(value.c_str());
|
||||
}
|
||||
|
||||
int SettingsBase::getSettingInMicrons(std::string key)
|
||||
{
|
||||
std::string value = getSettingString(key);
|
||||
return atof(value.c_str()) * 1000.0;
|
||||
}
|
||||
|
||||
double SettingsBase::getSettingInAngleRadians(std::string key)
|
||||
{
|
||||
std::string value = getSettingString(key);
|
||||
return atof(value.c_str()) / 180.0 * M_PI;
|
||||
}
|
||||
|
||||
bool SettingsBase::getSettingBoolean(std::string key)
|
||||
{
|
||||
std::string value = getSettingString(key);
|
||||
if (value == "on")
|
||||
return true;
|
||||
if (value == "yes")
|
||||
return true;
|
||||
if (value == "true" or value == "True") //Python uses "True"
|
||||
return true;
|
||||
return atoi(value.c_str()) != 0;
|
||||
}
|
||||
|
||||
double SettingsBase::getSettingInDegreeCelsius(std::string key)
|
||||
{
|
||||
std::string value = getSettingString(key);
|
||||
return atof(value.c_str());
|
||||
}
|
||||
|
||||
double SettingsBase::getSettingInMillimetersPerSecond(std::string key)
|
||||
{
|
||||
std::string value = getSettingString(key);
|
||||
return std::max(1.0, atof(value.c_str()));
|
||||
}
|
||||
|
||||
double SettingsBase::getSettingInPercentage(std::string key)
|
||||
{
|
||||
std::string value = getSettingString(key);
|
||||
return std::max(0.0, atof(value.c_str()));
|
||||
}
|
||||
|
||||
double SettingsBase::getSettingInSeconds(std::string key)
|
||||
{
|
||||
std::string value = getSettingString(key);
|
||||
return std::max(0.0, atof(value.c_str()));
|
||||
}
|
||||
|
||||
EGCodeFlavor SettingsBase::getSettingAsGCodeFlavor(std::string key)
|
||||
{
|
||||
std::string value = getSettingString(key);
|
||||
if (value == "RepRap")
|
||||
return GCODE_FLAVOR_REPRAP;
|
||||
else if (value == "UltiGCode")
|
||||
return GCODE_FLAVOR_ULTIGCODE;
|
||||
else if (value == "Makerbot")
|
||||
return GCODE_FLAVOR_MAKERBOT;
|
||||
else if (value == "BFB")
|
||||
return GCODE_FLAVOR_BFB;
|
||||
else if (value == "MACH3")
|
||||
return GCODE_FLAVOR_MACH3;
|
||||
else if (value == "RepRap (Volumatric)")
|
||||
return GCODE_FLAVOR_REPRAP_VOLUMATRIC;
|
||||
return GCODE_FLAVOR_REPRAP;
|
||||
}
|
||||
|
||||
EFillMethod SettingsBase::getSettingAsFillMethod(std::string key)
|
||||
{
|
||||
std::string value = getSettingString(key);
|
||||
if (value == "Lines")
|
||||
return Fill_Lines;
|
||||
if (value == "Grid")
|
||||
return Fill_Grid;
|
||||
if (value == "Triangles")
|
||||
return Fill_Triangles;
|
||||
if (value == "Concentric")
|
||||
return Fill_Concentric;
|
||||
if (value == "ZigZag")
|
||||
return Fill_ZigZag;
|
||||
return Fill_None;
|
||||
}
|
||||
|
||||
EPlatformAdhesion SettingsBase::getSettingAsPlatformAdhesion(std::string key)
|
||||
{
|
||||
std::string value = getSettingString(key);
|
||||
if (value == "Brim")
|
||||
return Adhesion_Brim;
|
||||
if (value == "Raft")
|
||||
return Adhesion_Raft;
|
||||
return Adhesion_None;
|
||||
}
|
||||
|
||||
ESupportType SettingsBase::getSettingAsSupportType(std::string key)
|
||||
{
|
||||
std::string value = getSettingString(key);
|
||||
if (value == "Everywhere")
|
||||
return Support_Everywhere;
|
||||
if (value == "Touching Buildplate")
|
||||
return Support_PlatformOnly;
|
||||
return Support_None;
|
||||
}
|
||||
|
||||
+74
-138
@@ -2,6 +2,7 @@
|
||||
#define SETTINGS_H
|
||||
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
#include "utils/floatpoint.h"
|
||||
|
||||
@@ -9,30 +10,11 @@
|
||||
#define VERSION "DEV"
|
||||
#endif
|
||||
|
||||
#define FIX_HORRIBLE_UNION_ALL_TYPE_A 0x01
|
||||
#define FIX_HORRIBLE_UNION_ALL_TYPE_B 0x02
|
||||
#define FIX_HORRIBLE_EXTENSIVE_STITCHING 0x04
|
||||
#define FIX_HORRIBLE_UNION_ALL_TYPE_C 0x08
|
||||
#define FIX_HORRIBLE_KEEP_NONE_CLOSED 0x10
|
||||
|
||||
/**
|
||||
* Type of support material.
|
||||
* Grid is a X/Y grid with an outline, which is very strong, provides good support. But in some cases is hard to remove.
|
||||
* Lines give a row of lines which break off one at a time, making them easier to remove, but they do not support as good as the grid support.
|
||||
/*!
|
||||
* Different flavors of GCode. Some machines require different types of GCode.
|
||||
* The GCode flavor definition handles this as a big setting to make major or minor modifications to the GCode.
|
||||
*/
|
||||
enum Support_Pattern
|
||||
{
|
||||
SUPPORT_TYPE_GRID = 0,
|
||||
SUPPORT_TYPE_LINES = 1
|
||||
};
|
||||
|
||||
#ifndef DEFAULT_CONFIG_PATH
|
||||
#define DEFAULT_CONFIG_PATH "default.cfg"
|
||||
#endif
|
||||
|
||||
#define CONFIG_MULTILINE_SEPARATOR "\"\"\""
|
||||
|
||||
enum GCode_Flavor
|
||||
enum EGCodeFlavor
|
||||
{
|
||||
/**
|
||||
* RepRap flavored GCode is Marlin/Sprinter/Repetier based GCode.
|
||||
@@ -88,129 +70,83 @@ enum GCode_Flavor
|
||||
GCODE_FLAVOR_REPRAP_VOLUMATRIC = 5,
|
||||
};
|
||||
|
||||
/*!
|
||||
* In Cura different infill methods are available.
|
||||
* This enum defines which fill patterns are available to get a uniform naming troughout the engine.
|
||||
* The different methods are used for top/bottom, support and sparse infill.
|
||||
*/
|
||||
enum EFillMethod
|
||||
{
|
||||
Fill_Lines,
|
||||
Fill_Grid,
|
||||
Fill_Triangles,
|
||||
Fill_Concentric,
|
||||
Fill_ZigZag,
|
||||
Fill_None
|
||||
};
|
||||
|
||||
/*!
|
||||
* Type of platform adheasion
|
||||
*/
|
||||
enum EPlatformAdhesion
|
||||
{
|
||||
Adhesion_None,
|
||||
Adhesion_Brim,
|
||||
Adhesion_Raft
|
||||
};
|
||||
|
||||
/*!
|
||||
* Type of support material to generate
|
||||
*/
|
||||
enum ESupportType
|
||||
{
|
||||
Support_None,
|
||||
Support_PlatformOnly,
|
||||
Support_Everywhere
|
||||
};
|
||||
|
||||
#define MAX_EXTRUDERS 16
|
||||
|
||||
/**
|
||||
* Type of infill pattern.
|
||||
//Maximum number of sparse layers that can be combined into a single sparse extrusion.
|
||||
#define MAX_SPARSE_COMBINE 8
|
||||
|
||||
/*!
|
||||
* Base class for every object that can hold settings.
|
||||
* The SettingBase object can hold multiple key-value pairs that define settings.
|
||||
* The settings that are set on a SettingBase are checked against the SettingRegistry to ensure keys are valid.
|
||||
* Different conversion functions are available for settings to increase code clarity and in the future make
|
||||
* unit conversions possible.
|
||||
*/
|
||||
enum Infill_Pattern
|
||||
{
|
||||
INFILL_AUTOMATIC = 0,
|
||||
INFILL_GRID = 1,
|
||||
INFILL_LINES = 2,
|
||||
INFILL_CONCENTRIC = 3,
|
||||
};
|
||||
|
||||
class _ConfigSettingIndex
|
||||
{
|
||||
public:
|
||||
const char* key;
|
||||
int* ptr;
|
||||
|
||||
_ConfigSettingIndex(const char* key, int* ptr) : key(key), ptr(ptr) {}
|
||||
};
|
||||
|
||||
class ConfigSettings
|
||||
class SettingsBase
|
||||
{
|
||||
private:
|
||||
std::vector<_ConfigSettingIndex> _index;
|
||||
std::map<std::string, std::string> setting_values;
|
||||
SettingsBase* parent;
|
||||
public:
|
||||
static ConfigSettings *config; // allow access to config settings from everywhere
|
||||
int layerThickness;
|
||||
int initialLayerThickness;
|
||||
int filamentDiameter;
|
||||
int filamentFlow;
|
||||
int layer0extrusionWidth;
|
||||
int extrusionWidth;
|
||||
int insetCount;
|
||||
int downSkinCount;
|
||||
int upSkinCount;
|
||||
int skirtDistance;
|
||||
int skirtLineCount;
|
||||
int skirtMinLength;
|
||||
SettingsBase();
|
||||
SettingsBase(SettingsBase* parent);
|
||||
|
||||
//Retraction settings
|
||||
int retractionAmount;
|
||||
int retractionAmountPrime;
|
||||
int retractionAmountExtruderSwitch;
|
||||
int retractionSpeed;
|
||||
int retractionMinimalDistance;
|
||||
int minimalExtrusionBeforeRetraction;
|
||||
int retractionZHop;
|
||||
bool hasSetting(std::string key);
|
||||
|
||||
int enableCombing;
|
||||
int enableOozeShield;
|
||||
int wipeTowerSize;
|
||||
int multiVolumeOverlap;
|
||||
|
||||
int initialSpeedupLayers;
|
||||
int initialLayerSpeed;
|
||||
int printSpeed;
|
||||
int inset0Speed;
|
||||
int insetXSpeed;
|
||||
int moveSpeed;
|
||||
int fanFullOnLayerNr;
|
||||
|
||||
//Infill settings
|
||||
int sparseInfillLineDistance;
|
||||
int infillOverlap;
|
||||
int infillSpeed;
|
||||
int infillPattern;
|
||||
|
||||
//Support material
|
||||
int supportType;
|
||||
int supportAngle;
|
||||
int supportEverywhere;
|
||||
int supportLineDistance;
|
||||
int supportXYDistance;
|
||||
int supportZDistance;
|
||||
int supportExtruder;
|
||||
|
||||
//Cool settings
|
||||
int minimalLayerTime;
|
||||
int minimalFeedrate;
|
||||
int coolHeadLift;
|
||||
int fanSpeedMin;
|
||||
int fanSpeedMax;
|
||||
|
||||
//Raft settings
|
||||
int raftMargin;
|
||||
int raftLineSpacing;
|
||||
int raftBaseThickness;
|
||||
int raftBaseLinewidth;
|
||||
int raftBaseSpeed;
|
||||
int raftInterfaceThickness;
|
||||
int raftInterfaceLinewidth;
|
||||
int raftInterfaceLineSpacing;
|
||||
int raftFanSpeed;
|
||||
int raftSurfaceThickness;
|
||||
int raftSurfaceLinewidth;
|
||||
int raftSurfaceLineSpacing;
|
||||
int raftSurfaceLayers;
|
||||
int raftSurfaceSpeed;
|
||||
int raftAirGap;
|
||||
int raftAirGapLayer0;
|
||||
|
||||
FMatrix3x3 matrix;
|
||||
IntPoint objectPosition;
|
||||
int objectSink;
|
||||
int autoCenter;
|
||||
|
||||
int fixHorrible;
|
||||
int spiralizeMode;
|
||||
int simpleMode;
|
||||
int gcodeFlavor;
|
||||
|
||||
IntPoint extruderOffset[MAX_EXTRUDERS];
|
||||
std::string startCode;
|
||||
std::string endCode;
|
||||
std::string preSwitchExtruderCode;
|
||||
std::string postSwitchExtruderCode;
|
||||
|
||||
ConfigSettings();
|
||||
bool setSetting(const char* key, const char* value);
|
||||
bool readSettings(void);
|
||||
bool readSettings(const char* path);
|
||||
void setSetting(std::string key, std::string value);
|
||||
|
||||
std::string getSettingString(std::string key);
|
||||
int getSettingAsIndex(std::string key);
|
||||
int getSettingAsCount(std::string key);
|
||||
|
||||
double getSettingInAngleRadians(std::string key);
|
||||
int getSettingInMicrons(std::string key);
|
||||
bool getSettingBoolean(std::string key);
|
||||
double getSettingInDegreeCelsius(std::string key);
|
||||
double getSettingInMillimetersPerSecond(std::string key);
|
||||
double getSettingInPercentage(std::string key);
|
||||
double getSettingInSeconds(std::string key);
|
||||
|
||||
EGCodeFlavor getSettingAsGCodeFlavor(std::string key);
|
||||
EFillMethod getSettingAsFillMethod(std::string key);
|
||||
EPlatformAdhesion getSettingAsPlatformAdhesion(std::string key);
|
||||
ESupportType getSettingAsSupportType(std::string key);
|
||||
};
|
||||
|
||||
|
||||
#endif//SETTINGS_H
|
||||
|
||||
+128
-64
@@ -1,9 +1,24 @@
|
||||
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
|
||||
#include "skin.h"
|
||||
#include "polygonOptimizer.h"
|
||||
#include "utils/polygonUtils.h"
|
||||
|
||||
namespace cura {
|
||||
namespace cura
|
||||
{
|
||||
|
||||
void generateSkins(int layerNr, SliceVolumeStorage& storage, int extrusionWidth, int downSkinCount, int upSkinCount, int infillOverlap)
|
||||
void generateSkins(int layerNr, SliceMeshStorage& storage, int extrusionWidth, int downSkinCount, int upSkinCount, int insetCount, bool avoidOverlappingPerimeters)
|
||||
{
|
||||
generateSkinAreas(layerNr, storage, extrusionWidth, downSkinCount, upSkinCount);
|
||||
|
||||
SliceLayer* layer = &storage.layers[layerNr];
|
||||
for(unsigned int partNr=0; partNr<layer->parts.size(); partNr++)
|
||||
{
|
||||
SliceLayerPart* part = &layer->parts[partNr];
|
||||
generateSkinInsets(part, extrusionWidth, insetCount, avoidOverlappingPerimeters);
|
||||
}
|
||||
}
|
||||
|
||||
void generateSkinAreas(int layerNr, SliceMeshStorage& storage, int extrusionWidth, int downSkinCount, int upSkinCount)
|
||||
{
|
||||
SliceLayer* layer = &storage.layers[layerNr];
|
||||
|
||||
@@ -11,109 +26,158 @@ void generateSkins(int layerNr, SliceVolumeStorage& storage, int extrusionWidth,
|
||||
{
|
||||
SliceLayerPart* part = &layer->parts[partNr];
|
||||
|
||||
Polygons upskin = part->insets[part->insets.size() - 1].offset(-extrusionWidth/2);
|
||||
Polygons upskin = part->insets.back().offset(-extrusionWidth/2);
|
||||
Polygons downskin = upskin;
|
||||
|
||||
|
||||
if (part->insets.size() > 1)
|
||||
{
|
||||
//Add thin wall filling by taking the area between the insets.
|
||||
Polygons thinWalls = part->insets[0].offset(-extrusionWidth / 2 - extrusionWidth * infillOverlap / 100).difference(part->insets[1].offset(extrusionWidth * 6 / 10));
|
||||
upskin.add(thinWalls);
|
||||
downskin.add(thinWalls);
|
||||
}
|
||||
if (static_cast<int>(layerNr - downSkinCount) >= 0)
|
||||
{
|
||||
SliceLayer* layer2 = &storage.layers[layerNr - downSkinCount];
|
||||
for(unsigned int partNr2=0; partNr2<layer2->parts.size(); partNr2++)
|
||||
for(SliceLayerPart& part2 : layer2->parts)
|
||||
{
|
||||
if (part->boundaryBox.hit(layer2->parts[partNr2].boundaryBox))
|
||||
downskin = downskin.difference(layer2->parts[partNr2].insets[layer2->parts[partNr2].insets.size() - 1]);
|
||||
if (part->boundaryBox.hit(part2.boundaryBox))
|
||||
downskin = downskin.difference(part2.insets.back());
|
||||
}
|
||||
}
|
||||
if (static_cast<int>(layerNr + upSkinCount) < static_cast<int>(storage.layers.size()))
|
||||
{
|
||||
SliceLayer* layer2 = &storage.layers[layerNr + upSkinCount];
|
||||
for(unsigned int partNr2=0; partNr2<layer2->parts.size(); partNr2++)
|
||||
for(SliceLayerPart& part2 : layer2->parts)
|
||||
{
|
||||
if (part->boundaryBox.hit(layer2->parts[partNr2].boundaryBox))
|
||||
upskin = upskin.difference(layer2->parts[partNr2].insets[layer2->parts[partNr2].insets.size() - 1]);
|
||||
if (part->boundaryBox.hit(part2.boundaryBox))
|
||||
upskin = upskin.difference(part2.insets.back());
|
||||
}
|
||||
}
|
||||
|
||||
part->skinOutline = upskin.unionPolygons(downskin);
|
||||
|
||||
double minAreaSize = (2 * M_PI * INT2MM(extrusionWidth) * INT2MM(extrusionWidth)) * 0.3;
|
||||
for(unsigned int i=0; i<part->skinOutline.size(); i++)
|
||||
Polygons skin = upskin.unionPolygons(downskin);
|
||||
|
||||
double minAreaSize = (2 * M_PI * INT2MM(extrusionWidth) * INT2MM(extrusionWidth)) * 0.3; // TODO: hardcoded value!
|
||||
skin.removeSmallAreas(minAreaSize);
|
||||
|
||||
for (Polygons& skin_area_part : skin.splitIntoParts())
|
||||
{
|
||||
double area = INT2MM(INT2MM(fabs(part->skinOutline[i].area())));
|
||||
if (area < minAreaSize) // Only create an up/down skin if the area is large enough. So you do not create tiny blobs of "trying to fill"
|
||||
part->skin_parts.emplace_back();
|
||||
part->skin_parts.back().outline = skin_area_part;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void generateSkinInsets(SliceLayerPart* part, int extrusionWidth, int insetCount, bool avoidOverlappingPerimeters)
|
||||
{
|
||||
if (insetCount == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (SkinPart& skin_part : part->skin_parts)
|
||||
{
|
||||
for(int i=0; i<insetCount; i++)
|
||||
{
|
||||
skin_part.insets.push_back(Polygons());
|
||||
if (i == 0)
|
||||
{
|
||||
part->skinOutline.remove(i);
|
||||
i -= 1;
|
||||
offsetSafe(skin_part.outline, - extrusionWidth/2, extrusionWidth, skin_part.insets[0], avoidOverlappingPerimeters);
|
||||
Polygons in_between = skin_part.outline.difference(skin_part.insets[0].offset(extrusionWidth/2));
|
||||
skin_part.perimeterGaps.add(in_between);
|
||||
} else
|
||||
{
|
||||
offsetExtrusionWidth(skin_part.insets[i-1], true, extrusionWidth, skin_part.insets[i], &skin_part.perimeterGaps, avoidOverlappingPerimeters);
|
||||
}
|
||||
|
||||
optimizePolygons(skin_part.insets[i]);
|
||||
if (skin_part.insets[i].size() < 1)
|
||||
{
|
||||
skin_part.insets.pop_back();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void generateSparse(int layerNr, SliceVolumeStorage& storage, int extrusionWidth, int downSkinCount, int upSkinCount)
|
||||
void generateSparse(int layerNr, SliceMeshStorage& storage, int extrusionWidth, int infill_skin_overlap)
|
||||
{
|
||||
SliceLayer& layer = storage.layers[layerNr];
|
||||
|
||||
for(SliceLayerPart& part : layer.parts)
|
||||
{
|
||||
Polygons sparse = part.insets.back().offset(-extrusionWidth / 2 - infill_skin_overlap);
|
||||
|
||||
for(SliceLayerPart& part2 : layer.parts)
|
||||
{
|
||||
if (part.boundaryBox.hit(part2.boundaryBox))
|
||||
{
|
||||
for(SkinPart& skin_part : part2.skin_parts)
|
||||
{
|
||||
sparse = sparse.difference(skin_part.outline);
|
||||
}
|
||||
}
|
||||
}
|
||||
sparse.removeSmallAreas(3.0);//(2 * M_PI * INT2MM(config.extrusionWidth) * INT2MM(config.extrusionWidth)) * 3;
|
||||
|
||||
part.sparse_outline.push_back(sparse.offset(infill_skin_overlap));
|
||||
}
|
||||
}
|
||||
|
||||
void combineSparseLayers(int layerNr, SliceMeshStorage& storage, int amount)
|
||||
{
|
||||
SliceLayer* layer = &storage.layers[layerNr];
|
||||
|
||||
for(unsigned int partNr=0; partNr<layer->parts.size(); partNr++)
|
||||
for(int n=1; n<amount; n++)
|
||||
{
|
||||
SliceLayerPart* part = &layer->parts[partNr];
|
||||
|
||||
Polygons sparse = part->insets[part->insets.size() - 1].offset(-extrusionWidth/2);
|
||||
Polygons downskin = sparse;
|
||||
Polygons upskin = sparse;
|
||||
if (layerNr < n)
|
||||
break;
|
||||
|
||||
if (static_cast<int>(layerNr - downSkinCount) >= 0)
|
||||
SliceLayer* layer2 = &storage.layers[layerNr - n];
|
||||
for(SliceLayerPart& part : layer->parts)
|
||||
{
|
||||
SliceLayer* layer2 = &storage.layers[layerNr - downSkinCount];
|
||||
for(unsigned int partNr2=0; partNr2<layer2->parts.size(); partNr2++)
|
||||
Polygons result;
|
||||
for(SliceLayerPart& part2 : layer2->parts)
|
||||
{
|
||||
if (part->boundaryBox.hit(layer2->parts[partNr2].boundaryBox))
|
||||
if (part.boundaryBox.hit(part2.boundaryBox))
|
||||
{
|
||||
if (layer2->parts[partNr2].insets.size() > 1)
|
||||
{
|
||||
downskin = downskin.difference(layer2->parts[partNr2].insets[layer2->parts[partNr2].insets.size() - 2]);
|
||||
}else{
|
||||
downskin = downskin.difference(layer2->parts[partNr2].insets[layer2->parts[partNr2].insets.size() - 1]);
|
||||
}
|
||||
Polygons intersection = part.sparse_outline[n - 1].intersection(part2.sparse_outline[0]).offset(-200).offset(200);
|
||||
result.add(intersection);
|
||||
part.sparse_outline[n - 1] = part.sparse_outline[n - 1].difference(intersection);
|
||||
part2.sparse_outline[0] = part2.sparse_outline[0].difference(intersection);
|
||||
}
|
||||
}
|
||||
|
||||
part.sparse_outline.push_back(result);
|
||||
}
|
||||
if (static_cast<int>(layerNr + upSkinCount) < static_cast<int>(storage.layers.size()))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void generatePerimeterGaps(int layer_nr, SliceMeshStorage& storage, int extrusionWidth, int downSkinCount, int upSkinCount)
|
||||
{
|
||||
SliceLayer& layer = storage.layers[layer_nr];
|
||||
|
||||
for (SliceLayerPart& part : layer.parts)
|
||||
{ // handle gaps between perimeters etc.
|
||||
if (downSkinCount > 0 && upSkinCount > 0 && // note: if both are zero or less, then all gaps will be used
|
||||
layer_nr >= downSkinCount && layer_nr < static_cast<int>(storage.layers.size() - upSkinCount)) // remove gaps which appear within print, i.e. not on the bottom most or top most skin
|
||||
{
|
||||
SliceLayer* layer2 = &storage.layers[layerNr + upSkinCount];
|
||||
for(unsigned int partNr2=0; partNr2<layer2->parts.size(); partNr2++)
|
||||
Polygons outlines_above;
|
||||
for (SliceLayerPart& part_above : storage.layers[layer_nr + upSkinCount].parts)
|
||||
{
|
||||
if (part->boundaryBox.hit(layer2->parts[partNr2].boundaryBox))
|
||||
if (part.boundaryBox.hit(part_above.boundaryBox))
|
||||
{
|
||||
if (layer2->parts[partNr2].insets.size() > 1)
|
||||
{
|
||||
upskin = upskin.difference(layer2->parts[partNr2].insets[layer2->parts[partNr2].insets.size() - 2]);
|
||||
}else{
|
||||
upskin = upskin.difference(layer2->parts[partNr2].insets[layer2->parts[partNr2].insets.size() - 1]);
|
||||
}
|
||||
outlines_above.add(part_above.outline);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Polygons result = upskin.unionPolygons(downskin);
|
||||
|
||||
double minAreaSize = 3.0;//(2 * M_PI * INT2MM(config.extrusionWidth) * INT2MM(config.extrusionWidth)) * 3;
|
||||
for(unsigned int i=0; i<result.size(); i++)
|
||||
{
|
||||
double area = INT2MM(INT2MM(fabs(result[i].area())));
|
||||
if (area < minAreaSize) /* Only create an up/down skin if the area is large enough. So you do not create tiny blobs of "trying to fill" */
|
||||
Polygons outlines_below;
|
||||
for (SliceLayerPart& part_below : storage.layers[layer_nr - downSkinCount].parts)
|
||||
{
|
||||
result.remove(i);
|
||||
i -= 1;
|
||||
if (part.boundaryBox.hit(part_below.boundaryBox))
|
||||
{
|
||||
outlines_below.add(part_below.outline);
|
||||
}
|
||||
}
|
||||
part.perimeterGaps = part.perimeterGaps.intersection(outlines_above.xorPolygons(outlines_below));
|
||||
}
|
||||
|
||||
part->sparseOutline = sparse.difference(result);
|
||||
double minAreaSize = (2 * M_PI * INT2MM(extrusionWidth) * INT2MM(extrusionWidth)) * 0.3; // TODO: hardcoded value!
|
||||
part.perimeterGaps.removeSmallAreas(minAreaSize);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+57
-3
@@ -4,10 +4,64 @@
|
||||
|
||||
#include "sliceDataStorage.h"
|
||||
|
||||
namespace cura {
|
||||
namespace cura
|
||||
{
|
||||
|
||||
void generateSkins(int layerNr, SliceVolumeStorage& storage, int extrusionWidth, int downSkinCount, int upSkinCount, int infillOverlap);
|
||||
void generateSparse(int layerNr, SliceVolumeStorage& storage, int extrusionWidth, int downSkinCount, int upSkinCount);
|
||||
/*!
|
||||
* Generate the gap areas which occur between consecutive insets.
|
||||
*
|
||||
* \param layerNr The index of the layer for which to generate the gaps.
|
||||
* \param storage The storage where the layer outline information (input) is stored and where the gap areas (output) are stored.
|
||||
* \param extrusionWidth extrusionWidth
|
||||
* \param downSkinCount The number of layers of bottom gaps
|
||||
* \param upSkinCount The number of layers of top gaps
|
||||
*/
|
||||
void generatePerimeterGaps(int layerNr, SliceMeshStorage& storage, int extrusionWidth, int downSkinCount, int upSkinCount);
|
||||
|
||||
/*!
|
||||
* Generate the skin areas and its insets.
|
||||
*
|
||||
* \param layerNr The index of the layer for which to generate the skins.
|
||||
* \param storage The storage where the layer outline information (input) is stored and where the skin insets and fill areas (output) are stored.
|
||||
* \param extrusionWidth extrusionWidth
|
||||
* \param downSkinCount The number of layers of bottom skin
|
||||
* \param upSkinCount The number of layers of top skin
|
||||
* \param insetCount The number of perimeters to surround the skin
|
||||
* \param avoidOverlappingPerimeters Whether to remove the parts of two consecutive perimeters where they have overlap (and store the gaps thus created in the \p storage)
|
||||
*/
|
||||
void generateSkins(int layerNr, SliceMeshStorage& storage, int extrusionWidth, int downSkinCount, int upSkinCount, int insetCount, bool avoidOverlappingPerimeters);
|
||||
|
||||
/*!
|
||||
* Generate the skin areas (outlines)
|
||||
*
|
||||
* \param layerNr The index of the layer for which to generate the skins.
|
||||
* \param storage The storage where the layer outline information (input) is stored and where the skin outline (output) is stored.
|
||||
* \param extrusionWidth extrusionWidth
|
||||
* \param downSkinCount The number of layers of bottom skin
|
||||
* \param upSkinCount The number of layers of top skin
|
||||
*/
|
||||
void generateSkinAreas(int layerNr, SliceMeshStorage& storage, int extrusionWidth, int downSkinCount, int upSkinCount);
|
||||
|
||||
/*!
|
||||
* Generate the skin insets.
|
||||
*
|
||||
* \param layerNr The index of the layer for which to generate the skins.
|
||||
* \param part The part where the skin outline information (input) is stored and where the skin insets (output) are stored.
|
||||
* \param extrusionWidth extrusionWidth
|
||||
* \param insetCount The number of perimeters to surround the skin
|
||||
* \param avoidOverlappingPerimeters Whether to remove the parts of two consecutive perimeters where they have overlap (and store the gaps thus created in the \p storage)
|
||||
*/
|
||||
void generateSkinInsets(SliceLayerPart* part, int extrusionWidth, int insetCount, bool avoidOverlappingPerimeters);
|
||||
|
||||
/*!
|
||||
* Generate Infill
|
||||
* \param layerNr The index of the layer for which to generate the infill
|
||||
* \param part The part where the insets (input) are stored and where the infill (output) is stored.
|
||||
* \param extrusionWidth width of the wall lines
|
||||
* \param infill_skin_overlap overlap distance between infill and skin
|
||||
*/
|
||||
void generateSparse(int layerNr, SliceMeshStorage& storage, int extrusionWidth, int infill_skin_overlap);
|
||||
void combineSparseLayers(int layerNr, SliceMeshStorage& storage, int amount);
|
||||
|
||||
}//namespace cura
|
||||
|
||||
|
||||
+60
-20
@@ -2,41 +2,67 @@
|
||||
#include "skirt.h"
|
||||
#include "support.h"
|
||||
|
||||
namespace cura {
|
||||
|
||||
void generateSkirt(SliceDataStorage& storage, int distance, int extrusionWidth, int count, int minLength, int initialLayerHeight)
|
||||
namespace cura
|
||||
{
|
||||
|
||||
void generateSkirt(SliceDataStorage& storage, int distance, int extrusionWidth, int count, int minLength)
|
||||
{
|
||||
if (count == 0) return;
|
||||
|
||||
bool externalOnly = (distance > 0);
|
||||
|
||||
Polygons support;
|
||||
if (storage.support.generated)
|
||||
support = storage.support.supportAreasPerLayer[0];
|
||||
{ // get support polygons
|
||||
for(SliceMeshStorage& mesh : storage.meshes)
|
||||
{
|
||||
if (mesh.layers.size() < 1) continue;
|
||||
SliceLayer* layer = &mesh.layers[0];
|
||||
for(unsigned int i=0; i<layer->parts.size(); i++)
|
||||
support = support.difference(layer->parts[i].outline);
|
||||
}
|
||||
|
||||
// expand and contract to smooth the final polygon
|
||||
if (count == 1 && distance > 0)
|
||||
{
|
||||
int dist = extrusionWidth * 5;
|
||||
support = support.offset(dist).offset(-dist);
|
||||
}
|
||||
}
|
||||
|
||||
int overshoot = 0; // distance by which to expand and contract the skirt to approximate the convex hull of the first layer
|
||||
if (count == 1 && distance > 0)
|
||||
{
|
||||
overshoot = 100000; // 10 cm
|
||||
}
|
||||
|
||||
|
||||
for(int skirtNr=0; skirtNr<count;skirtNr++)
|
||||
{
|
||||
int offsetDistance = distance + extrusionWidth * skirtNr + extrusionWidth / 2;
|
||||
|
||||
SupportPolyGenerator supportGenerator(storage.support, initialLayerHeight);
|
||||
int offsetDistance = distance + extrusionWidth * skirtNr + extrusionWidth / 2 + overshoot;
|
||||
|
||||
Polygons skirtPolygons(storage.wipeTower.offset(offsetDistance));
|
||||
for(unsigned int volumeIdx = 0; volumeIdx < storage.volumes.size(); volumeIdx++)
|
||||
for(SliceMeshStorage& mesh : storage.meshes)
|
||||
{
|
||||
if (storage.volumes[volumeIdx].layers.size() < 1) continue;
|
||||
SliceLayer* layer = &storage.volumes[volumeIdx].layers[0];
|
||||
if (mesh.layers.size() < 1) continue;
|
||||
SliceLayer* layer = &mesh.layers[0];
|
||||
for(unsigned int i=0; i<layer->parts.size(); i++)
|
||||
{
|
||||
if (externalOnly)
|
||||
{
|
||||
Polygons p;
|
||||
p.add(layer->parts[i].outline[0]);
|
||||
skirtPolygons = skirtPolygons.unionPolygons(p.offset(offsetDistance));
|
||||
skirtPolygons = skirtPolygons.unionPolygons(p.offset(offsetDistance, ClipperLib::jtRound));
|
||||
}
|
||||
else
|
||||
skirtPolygons = skirtPolygons.unionPolygons(layer->parts[i].outline.offset(offsetDistance));
|
||||
|
||||
supportGenerator.polygons = supportGenerator.polygons.difference(layer->parts[i].outline);
|
||||
{
|
||||
skirtPolygons = skirtPolygons.unionPolygons(layer->parts[i].outline.offset(offsetDistance, ClipperLib::jtRound));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Contract and expand the suppory polygons so small sections are removed and the final polygon is smoothed a bit.
|
||||
supportGenerator.polygons = supportGenerator.polygons.offset(-extrusionWidth * 3);
|
||||
supportGenerator.polygons = supportGenerator.polygons.offset(extrusionWidth * 3);
|
||||
skirtPolygons = skirtPolygons.unionPolygons(supportGenerator.polygons.offset(offsetDistance));
|
||||
|
||||
skirtPolygons = skirtPolygons.unionPolygons(support.offset(offsetDistance, ClipperLib::jtRound));
|
||||
//Remove small inner skirt holes. Holes have a negative area, remove anything smaller then 100x extrusion "area"
|
||||
for(unsigned int n=0; n<skirtPolygons.size(); n++)
|
||||
{
|
||||
@@ -46,11 +72,25 @@ void generateSkirt(SliceDataStorage& storage, int distance, int extrusionWidth,
|
||||
}
|
||||
|
||||
storage.skirt.add(skirtPolygons);
|
||||
|
||||
|
||||
int lenght = storage.skirt.polygonLength();
|
||||
if (skirtNr + 1 >= count && lenght > 0 && lenght < minLength)
|
||||
if (skirtNr + 1 >= count && lenght > 0 && lenght < minLength) // make brim have more lines when total length is too small
|
||||
count++;
|
||||
}
|
||||
|
||||
|
||||
//Add a skirt under the wipetower to make it stick better.
|
||||
Polygons wipe_tower = storage.wipeTower.offset(-extrusionWidth / 2);
|
||||
while(wipe_tower.size() > 0)
|
||||
{
|
||||
storage.skirt.add(wipe_tower);
|
||||
wipe_tower = wipe_tower.offset(-extrusionWidth);
|
||||
}
|
||||
|
||||
if (overshoot > 0)
|
||||
{
|
||||
storage.skirt = storage.skirt.offset(-overshoot, ClipperLib::jtRound);
|
||||
}
|
||||
}
|
||||
|
||||
}//namespace cura
|
||||
|
||||
+12
-2
@@ -4,9 +4,19 @@
|
||||
|
||||
#include "sliceDataStorage.h"
|
||||
|
||||
namespace cura {
|
||||
namespace cura
|
||||
{
|
||||
|
||||
void generateSkirt(SliceDataStorage& storage, int distance, int extrusionWidth, int count, int minLength, int initialLayerHeight);
|
||||
/*!
|
||||
* Generate skirt or brim (depending on parameters); when \p distance > 0 and \p count == 1 the skirt is generated, which has slighly different configuration.
|
||||
*
|
||||
* \param storage Storage containing the parts at the first layer
|
||||
* \param distance The distance of the first outset from the parts at the first layer
|
||||
* \param extrusionWidth extrusionWidth
|
||||
* \param count Number of outsets / brim lines
|
||||
* \param minLength The minimum length the skirt should have (enforced by taking more outsets)
|
||||
*/
|
||||
void generateSkirt(SliceDataStorage& storage, int distance, int extrusionWidth, int count, int minLength);
|
||||
|
||||
}//namespace cura
|
||||
|
||||
|
||||
+70
-46
@@ -4,83 +4,107 @@
|
||||
|
||||
#include "utils/intpoint.h"
|
||||
#include "utils/polygon.h"
|
||||
#include "mesh.h"
|
||||
#include "gcodePlanner.h"
|
||||
|
||||
/*
|
||||
SliceData
|
||||
+ Layers[]
|
||||
+ LayerParts[]
|
||||
+ OutlinePolygons[]
|
||||
+ Insets[]
|
||||
+ Polygons[]
|
||||
+ SkinPolygons[]
|
||||
*/
|
||||
namespace cura {
|
||||
|
||||
namespace cura
|
||||
{
|
||||
/*!
|
||||
* A SkinPart is a connected area designated as top and/or bottom skin.
|
||||
* Surrounding each non-bridged skin area with an outline may result in better top skins.
|
||||
* It's filled during fffProcessor.processSliceData(.) and used in fffProcessor.writeGCode(.) to generate the final gcode.
|
||||
*/
|
||||
class SkinPart
|
||||
{
|
||||
public:
|
||||
Polygons outline; //!< The skinOutline is the area which needs to be 100% filled to generate a proper top&bottom filling. It's filled by the "skin" module.
|
||||
std::vector<Polygons> insets; //!< The skin can have perimeters so that the skin lines always start at a perimeter instead of in the middle of an infill cell.
|
||||
Polygons perimeterGaps; //!< The gaps introduced by avoidOverlappingPerimeters which would otherwise be overlapping perimeters.
|
||||
};
|
||||
/*!
|
||||
The SliceLayerPart is a single enclosed printable area for a single layer. (Also known as islands)
|
||||
It's filled during the fffProcessor.processSliceData(.), where each step uses data from the previous steps.
|
||||
Finally it's used in the fffProcessor.writeGCode(.) to generate the final gcode.
|
||||
*/
|
||||
class SliceLayerPart
|
||||
{
|
||||
public:
|
||||
AABB boundaryBox;
|
||||
Polygons outline;
|
||||
Polygons combBoundery;
|
||||
vector<Polygons> insets;
|
||||
Polygons skinOutline;
|
||||
Polygons sparseOutline;
|
||||
AABB boundaryBox; //!< The boundaryBox is an axis-aligned bounardy box which is used to quickly check for possible collision between different parts on different layers. It's an optimalization used during skin calculations.
|
||||
Polygons outline; //!< The outline is the first member that is filled, and it's filled with polygons that match a cross section of the 3D model. The first polygon is the outer boundary polygon and the rest are holes.
|
||||
Polygons combBoundery; //!< The combBoundery is generated from the online. It's the area in which the nozzle tries to stay during traveling.
|
||||
std::vector<Polygons> insets; //!< The insets are generated with: an offset of (index * line_width + line_width/2) compared to the outline. The insets are also known as perimeters, and printed inside out.
|
||||
std::vector<SkinPart> skin_parts; //!< The skin parts which are filled for 100% with lines and/or insets.
|
||||
std::vector<Polygons> sparse_outline; //!< The sparse_outline are the areas which need to be filled with sparse (0-99%) infill. The sparse_outline is an array to support thicker layers of sparse infill. sparse_outline[n] is sparse outline of (n+1) layers thick.
|
||||
Polygons perimeterGaps; //!< The gaps introduced by avoidOverlappingPerimeters which would otherwise be overlapping perimeters.
|
||||
};
|
||||
|
||||
/*!
|
||||
The SlicerLayer contains all the data for a single cross section of the 3D model.
|
||||
*/
|
||||
class SliceLayer
|
||||
{
|
||||
public:
|
||||
int sliceZ;
|
||||
int printZ;
|
||||
vector<SliceLayerPart> parts;
|
||||
Polygons openLines;
|
||||
int sliceZ; //!< The height at which the 3D model was cut.
|
||||
// TODO: remove this /\ unused member!
|
||||
int printZ; //!< The height at which this layer needs to be printed. Can differ from sliceZ due to the raft.
|
||||
std::vector<SliceLayerPart> parts; //!< An array of LayerParts which contain the actual data. The parts are printed one at a time to minimize travel outside of the 3D model.
|
||||
Polygons openLines; //!< A list of lines which were never hooked up into a 2D polygon. (Currently unused in normal operation)
|
||||
};
|
||||
|
||||
/******************/
|
||||
class SupportPoint
|
||||
{
|
||||
public:
|
||||
int32_t z;
|
||||
double cosAngle;
|
||||
|
||||
SupportPoint(int32_t z, double cosAngle) : z(z), cosAngle(cosAngle) {}
|
||||
};
|
||||
class SupportStorage
|
||||
{
|
||||
public:
|
||||
bool generated;
|
||||
int angle;
|
||||
bool everywhere;
|
||||
int XYDistance;
|
||||
int ZDistance;
|
||||
|
||||
Point gridOffset;
|
||||
int32_t gridScale;
|
||||
int32_t gridWidth, gridHeight;
|
||||
vector<SupportPoint>* grid;
|
||||
SupportStorage(){grid = nullptr;}
|
||||
~SupportStorage(){if(grid) delete [] grid;}
|
||||
bool generated; //!< whether generateSupportGrid(.) has completed (successfully)
|
||||
|
||||
std::vector<Polygons> supportAreasPerLayer;
|
||||
|
||||
SupportStorage(){}
|
||||
~SupportStorage(){supportAreasPerLayer.clear(); }
|
||||
};
|
||||
/******************/
|
||||
|
||||
class SliceVolumeStorage
|
||||
class SliceMeshStorage
|
||||
{
|
||||
public:
|
||||
vector<SliceLayer> layers;
|
||||
SettingsBase* settings;
|
||||
std::vector<SliceLayer> layers;
|
||||
|
||||
RetractionConfig retraction_config;
|
||||
GCodePathConfig inset0_config;
|
||||
GCodePathConfig insetX_config;
|
||||
GCodePathConfig skin_config;
|
||||
GCodePathConfig infill_config[MAX_SPARSE_COMBINE];
|
||||
|
||||
SliceMeshStorage(SettingsBase* settings)
|
||||
: settings(settings), inset0_config(&retraction_config, "WALL-OUTER"), insetX_config(&retraction_config, "WALL-INNER"), skin_config(&retraction_config, "SKIN")
|
||||
{
|
||||
for(int n=0; n<MAX_SPARSE_COMBINE; n++)
|
||||
infill_config[n] = GCodePathConfig(&retraction_config, "FILL");
|
||||
}
|
||||
};
|
||||
|
||||
class SliceDataStorage
|
||||
{
|
||||
public:
|
||||
Point3 modelSize, modelMin, modelMax;
|
||||
Point3 model_size, model_min, model_max;
|
||||
Polygons skirt;
|
||||
Polygons raftOutline; //Storage for the outline of the raft. Will be filled with lines when the GCode is generated.
|
||||
vector<Polygons> oozeShield; //oozeShield per layer
|
||||
vector<SliceVolumeStorage> volumes;
|
||||
std::vector<Polygons> oozeShield; //oozeShield per layer
|
||||
std::vector<SliceMeshStorage> meshes;
|
||||
|
||||
RetractionConfig retraction_config;
|
||||
GCodePathConfig skirt_config;
|
||||
GCodePathConfig support_config;
|
||||
|
||||
SupportStorage support;
|
||||
Polygons wipeTower;
|
||||
Point wipePoint;
|
||||
|
||||
SliceDataStorage()
|
||||
: skirt_config(&retraction_config, "SKIRT"), support_config(&retraction_config, "SUPPORT")
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
}//namespace cura
|
||||
|
||||
+44
-91
@@ -9,8 +9,10 @@
|
||||
|
||||
namespace cura {
|
||||
|
||||
void SlicerLayer::makePolygons(OptimizedVolume* ov, bool keepNoneClosed, bool extensiveStitching)
|
||||
void SlicerLayer::makePolygons(Mesh* mesh, bool keep_none_closed, bool extensive_stitching)
|
||||
{
|
||||
Polygons openPolygonList;
|
||||
|
||||
for(unsigned int startSegment=0; startSegment < segmentList.size(); startSegment++)
|
||||
{
|
||||
if (segmentList[startSegment].addedToPolygon)
|
||||
@@ -28,20 +30,20 @@ void SlicerLayer::makePolygons(OptimizedVolume* ov, bool keepNoneClosed, bool ex
|
||||
Point p0 = segmentList[segmentIndex].end;
|
||||
poly.add(p0);
|
||||
int nextIndex = -1;
|
||||
OptimizedFace* face = &ov->faces[segmentList[segmentIndex].faceIndex];
|
||||
MeshFace* face = &mesh->faces[segmentList[segmentIndex].faceIndex];
|
||||
for(unsigned int i=0;i<3;i++)
|
||||
{
|
||||
if (face->touching[i] > -1 && faceToSegmentIndex.find(face->touching[i]) != faceToSegmentIndex.end())
|
||||
if (face->connected_face_index[i] > -1 && face_idx_to_segment_index.find(face->connected_face_index[i]) != face_idx_to_segment_index.end())
|
||||
{
|
||||
Point p1 = segmentList[faceToSegmentIndex[face->touching[i]]].start;
|
||||
Point p1 = segmentList[face_idx_to_segment_index[face->connected_face_index[i]]].start;
|
||||
Point diff = p0 - p1;
|
||||
if (shorterThen(diff, MM2INT(0.01)))
|
||||
{
|
||||
if (faceToSegmentIndex[face->touching[i]] == static_cast<int>(startSegment))
|
||||
if (face_idx_to_segment_index[face->connected_face_index[i]] == static_cast<int>(startSegment))
|
||||
canClose = true;
|
||||
if (segmentList[faceToSegmentIndex[face->touching[i]]].addedToPolygon)
|
||||
if (segmentList[face_idx_to_segment_index[face->connected_face_index[i]]].addedToPolygon)
|
||||
continue;
|
||||
nextIndex = faceToSegmentIndex[face->touching[i]];
|
||||
nextIndex = face_idx_to_segment_index[face->connected_face_index[i]];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -153,7 +155,7 @@ void SlicerLayer::makePolygons(OptimizedVolume* ov, bool keepNoneClosed, bool ex
|
||||
}
|
||||
}
|
||||
|
||||
if (extensiveStitching)
|
||||
if (extensive_stitching)
|
||||
{
|
||||
//For extensive stitching find 2 open polygons that are touching 2 closed polygons.
|
||||
// Then find the sortest path over this polygon that can be used to connect the open polygons,
|
||||
@@ -261,20 +263,7 @@ void SlicerLayer::makePolygons(OptimizedVolume* ov, bool keepNoneClosed, bool ex
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
int q=0;
|
||||
for(unsigned int i=0;i<openPolygonList.size();i++)
|
||||
{
|
||||
if (openPolygonList[i].size() < 2) continue;
|
||||
if (!q) log("***\n");
|
||||
log("S: %f %f\n", float(openPolygonList[i][0].X), float(openPolygonList[i][0].Y));
|
||||
log("E: %f %f\n", float(openPolygonList[i][openPolygonList[i].size()-1].X), float(openPolygonList[i][openPolygonList[i].size()-1].Y));
|
||||
q = 1;
|
||||
}
|
||||
*/
|
||||
//if (q) exit(1);
|
||||
|
||||
if (keepNoneClosed)
|
||||
if (keep_none_closed)
|
||||
{
|
||||
for(unsigned int n=0; n<openPolygonList.size(); n++)
|
||||
{
|
||||
@@ -282,15 +271,19 @@ void SlicerLayer::makePolygons(OptimizedVolume* ov, bool keepNoneClosed, bool ex
|
||||
polygonList.add(openPolygonList[n]);
|
||||
}
|
||||
}
|
||||
//Clear the openPolygonList to save memory, the only reason to keep it after this is for debugging.
|
||||
//openPolygonList.clear();
|
||||
|
||||
for(unsigned int i=0;i<openPolygonList.size();i++)
|
||||
{
|
||||
if (openPolygonList[i].size() > 0)
|
||||
openPolygons.newPoly() = openPolygonList[i];
|
||||
}
|
||||
|
||||
//Remove all the tiny polygons, or polygons that are not closed. As they do not contribute to the actual print.
|
||||
int snapDistance = MM2INT(1.0);
|
||||
for(unsigned int i=0;i<polygonList.size();i++)
|
||||
{
|
||||
int length = 0;
|
||||
|
||||
|
||||
for(unsigned int n=1; n<polygonList[i].size(); n++)
|
||||
{
|
||||
length += vSize(polygonList[i][n] - polygonList[i][n-1]);
|
||||
@@ -306,28 +299,34 @@ void SlicerLayer::makePolygons(OptimizedVolume* ov, bool keepNoneClosed, bool ex
|
||||
|
||||
//Finally optimize all the polygons. Every point removed saves time in the long run.
|
||||
optimizePolygons(polygonList);
|
||||
|
||||
polygonList = polygonList.removeDegenerateVerts(); // remove verts connected to overlapping line segments
|
||||
|
||||
int xy_offset = mesh->getSettingInMicrons("xy_offset");
|
||||
if (xy_offset != 0)
|
||||
{
|
||||
polygonList = polygonList.offset(xy_offset);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Slicer::Slicer(OptimizedVolume* ov, int32_t initial, int32_t thickness, bool keepNoneClosed, bool extensiveStitching)
|
||||
Slicer::Slicer(Mesh* mesh, int initial, int thickness, int layer_count, bool keep_none_closed, bool extensive_stitching)
|
||||
{
|
||||
modelSize = ov->model->modelSize;
|
||||
modelMin = ov->model->vMin;
|
||||
assert(layer_count > 0);
|
||||
|
||||
layers.resize(layer_count);
|
||||
|
||||
int layerCount = (modelSize.z - initial) / thickness + 1;
|
||||
cura::log("Layer count: %i\n", layerCount);
|
||||
layers.resize(layerCount);
|
||||
|
||||
for(int32_t layerNr = 0; layerNr < layerCount; layerNr++)
|
||||
for(int32_t layer_nr = 0; layer_nr < layer_count; layer_nr++)
|
||||
{
|
||||
layers[layerNr].z = initial + thickness * layerNr;
|
||||
layers[layer_nr].z = initial + thickness * layer_nr;
|
||||
}
|
||||
|
||||
for(unsigned int i=0; i<ov->faces.size(); i++)
|
||||
for(unsigned int i=0; i<mesh->faces.size(); i++)
|
||||
{
|
||||
Point3 p0 = ov->points[ov->faces[i].index[0]].p;
|
||||
Point3 p1 = ov->points[ov->faces[i].index[1]].p;
|
||||
Point3 p2 = ov->points[ov->faces[i].index[2]].p;
|
||||
MeshFace& face = mesh->faces[i];
|
||||
Point3 p0 = mesh->vertices[face.vertex_index[0]].p;
|
||||
Point3 p1 = mesh->vertices[face.vertex_index[1]].p;
|
||||
Point3 p2 = mesh->vertices[face.vertex_index[2]].p;
|
||||
int32_t minZ = p0.z;
|
||||
int32_t maxZ = p0.z;
|
||||
if (p1.z < minZ) minZ = p1.z;
|
||||
@@ -335,11 +334,11 @@ Slicer::Slicer(OptimizedVolume* ov, int32_t initial, int32_t thickness, bool kee
|
||||
if (p1.z > maxZ) maxZ = p1.z;
|
||||
if (p2.z > maxZ) maxZ = p2.z;
|
||||
|
||||
for(int32_t layerNr = (minZ - initial) / thickness; layerNr <= (maxZ - initial) / thickness; layerNr++)
|
||||
for(int32_t layer_nr = (minZ - initial) / thickness; layer_nr <= (maxZ - initial) / thickness; layer_nr++)
|
||||
{
|
||||
int32_t z = layerNr * thickness + initial;
|
||||
int32_t z = layer_nr * thickness + initial;
|
||||
if (z < minZ) continue;
|
||||
if (layerNr < 0) continue;
|
||||
if (layer_nr < 0) continue;
|
||||
|
||||
SlicerSegment s;
|
||||
if (p0.z < z && p1.z >= z && p2.z >= z)
|
||||
@@ -362,62 +361,16 @@ Slicer::Slicer(OptimizedVolume* ov, int32_t initial, int32_t thickness, bool kee
|
||||
// on the slice would create two segments
|
||||
continue;
|
||||
}
|
||||
layers[layerNr].faceToSegmentIndex[i] = layers[layerNr].segmentList.size();
|
||||
layers[layer_nr].face_idx_to_segment_index[i] = layers[layer_nr].segmentList.size();
|
||||
s.faceIndex = i;
|
||||
s.addedToPolygon = false;
|
||||
layers[layerNr].segmentList.push_back(s);
|
||||
layers[layer_nr].segmentList.push_back(s);
|
||||
}
|
||||
}
|
||||
|
||||
for(unsigned int layerNr=0; layerNr<layers.size(); layerNr++)
|
||||
for(unsigned int layer_nr=0; layer_nr<layers.size(); layer_nr++)
|
||||
{
|
||||
layers[layerNr].makePolygons(ov, keepNoneClosed, extensiveStitching);
|
||||
layers[layer_nr].makePolygons(mesh, keep_none_closed, extensive_stitching);
|
||||
}
|
||||
}
|
||||
|
||||
void Slicer::dumpSegmentsToHTML(const char* filename)
|
||||
{
|
||||
float scale = std::max(modelSize.x, modelSize.y) / 1500;
|
||||
FILE* f = fopen(filename, "w");
|
||||
fprintf(f, "<!DOCTYPE html><html><body>\n");
|
||||
for(unsigned int i=0; i<layers.size(); i++)
|
||||
{
|
||||
fprintf(f, "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" style='width:%ipx;height:%ipx'>\n", int(modelSize.x / scale), int(modelSize.y / scale));
|
||||
fprintf(f, "<marker id='MidMarker' viewBox='0 0 10 10' refX='5' refY='5' markerUnits='strokeWidth' markerWidth='10' markerHeight='10' stroke='lightblue' stroke-width='2' fill='none' orient='auto'>");
|
||||
fprintf(f, "<path d='M 0 0 L 10 5 M 0 10 L 10 5'/>");
|
||||
fprintf(f, "</marker>");
|
||||
fprintf(f, "<g fill-rule='evenodd' style=\"fill: gray; stroke:black;stroke-width:1\">\n");
|
||||
fprintf(f, "<path marker-mid='url(#MidMarker)' d=\"");
|
||||
for(unsigned int j=0; j<layers[i].polygonList.size(); j++)
|
||||
{
|
||||
PolygonRef p = layers[i].polygonList[j];
|
||||
for(unsigned int n=0; n<p.size(); n++)
|
||||
{
|
||||
if (n == 0)
|
||||
fprintf(f, "M");
|
||||
else
|
||||
fprintf(f, "L");
|
||||
fprintf(f, "%f,%f ", float(p[n].X - modelMin.x)/scale, float(p[n].Y - modelMin.y)/scale);
|
||||
}
|
||||
fprintf(f, "Z\n");
|
||||
}
|
||||
fprintf(f, "\"/>");
|
||||
fprintf(f, "</g>\n");
|
||||
for(unsigned int j=0; j<layers[i].openPolygonList.size(); j++)
|
||||
{
|
||||
PolygonRef p = layers[i].openPolygonList[j];
|
||||
if (p.size() < 1) continue;
|
||||
fprintf(f, "<polyline marker-mid='url(#MidMarker)' points=\"");
|
||||
for(unsigned int n=0; n<p.size(); n++)
|
||||
{
|
||||
fprintf(f, "%f,%f ", float(p[n].X - modelMin.x)/scale, float(p[n].Y - modelMin.y)/scale);
|
||||
}
|
||||
fprintf(f, "\" style=\"fill: none; stroke:red;stroke-width:1\" />\n");
|
||||
}
|
||||
fprintf(f, "</svg>\n");
|
||||
}
|
||||
fprintf(f, "</body></html>");
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
}//namespace cura
|
||||
|
||||
+5
-6
@@ -2,7 +2,7 @@
|
||||
#ifndef SLICER_H
|
||||
#define SLICER_H
|
||||
|
||||
#include "optimizedModel.h"
|
||||
#include "mesh.h"
|
||||
#include "utils/polygon.h"
|
||||
/*
|
||||
The Slicer creates layers of polygons from an optimized 3D model.
|
||||
@@ -40,13 +40,13 @@ class SlicerLayer
|
||||
{
|
||||
public:
|
||||
std::vector<SlicerSegment> segmentList;
|
||||
std::map<int, int> faceToSegmentIndex;
|
||||
std::map<int, int> face_idx_to_segment_index;
|
||||
|
||||
int z;
|
||||
Polygons polygonList;
|
||||
Polygons openPolygonList;
|
||||
Polygons openPolygons;
|
||||
|
||||
void makePolygons(OptimizedVolume* ov, bool keepNoneClosed, bool extensiveStitching);
|
||||
void makePolygons(Mesh* mesh, bool keepNoneClosed, bool extensiveStitching);
|
||||
|
||||
private:
|
||||
gapCloserResult findPolygonGapCloser(Point ip0, Point ip1)
|
||||
@@ -142,9 +142,8 @@ class Slicer
|
||||
{
|
||||
public:
|
||||
std::vector<SlicerLayer> layers;
|
||||
Point3 modelSize, modelMin;
|
||||
|
||||
Slicer(OptimizedVolume* ov, int32_t initial, int32_t thickness, bool keepNoneClosed, bool extensiveStitching);
|
||||
Slicer(Mesh* mesh, int initial, int thickness, int layer_count, bool keepNoneClosed, bool extensiveStitching);
|
||||
|
||||
SlicerSegment project2D(Point3& p0, Point3& p1, Point3& p2, int32_t z) const
|
||||
{
|
||||
|
||||
+322
-170
@@ -1,190 +1,342 @@
|
||||
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
|
||||
#include "support.h"
|
||||
|
||||
namespace cura {
|
||||
#include <cmath> // sqrt
|
||||
#include <utility> // pair
|
||||
|
||||
template<typename T> inline void swap(T& p0, T& p1)
|
||||
namespace cura
|
||||
{
|
||||
T tmp = p0;
|
||||
p0 = p1;
|
||||
p1 = tmp;
|
||||
}
|
||||
|
||||
int cmp_SupportPoint(const void* a, const void* b)
|
||||
/*
|
||||
* Algorithm:
|
||||
* From top layer to bottom layer:
|
||||
* - find overhang by looking at the difference between two consucutive layers
|
||||
* - join with support areas from layer above
|
||||
* - subtract current layer
|
||||
* - use the result for the next lower support layer (without doing XY-distance and Z bottom distance, so that a single support beam may move around the model a bit => more stability)
|
||||
* - perform inset using X/Y-distance and bottom Z distance
|
||||
*
|
||||
* for support buildplate only: purge all support not connected to buildplate
|
||||
*/
|
||||
void generateSupportAreas(SliceDataStorage& storage, SliceMeshStorage* object, int layer_count)
|
||||
{
|
||||
return ((SupportPoint*)a)->z - ((SupportPoint*)b)->z;
|
||||
}
|
||||
|
||||
void generateSupportGrid(SupportStorage& storage, OptimizedModel* om, int supportAngle, bool supportEverywhere, int supportXYDistance, int supportZDistance)
|
||||
{
|
||||
storage.generated = false;
|
||||
if (supportAngle < 0)
|
||||
// given settings
|
||||
ESupportType support_type = object->settings->getSettingAsSupportType("support_type");
|
||||
|
||||
storage.support.generated = false;
|
||||
if (!object->settings->getSettingBoolean("support_enable"))
|
||||
return;
|
||||
storage.generated = true;
|
||||
|
||||
storage.gridOffset.X = om->vMin.x;
|
||||
storage.gridOffset.Y = om->vMin.y;
|
||||
storage.gridScale = 200;
|
||||
storage.gridWidth = (om->modelSize.x / storage.gridScale) + 1;
|
||||
storage.gridHeight = (om->modelSize.y / storage.gridScale) + 1;
|
||||
storage.grid = new vector<SupportPoint>[storage.gridWidth * storage.gridHeight];
|
||||
storage.angle = supportAngle;
|
||||
storage.everywhere = supportEverywhere;
|
||||
storage.XYDistance = supportXYDistance;
|
||||
storage.ZDistance = supportZDistance;
|
||||
|
||||
for(unsigned int volumeIdx = 0; volumeIdx < om->volumes.size(); volumeIdx++)
|
||||
{
|
||||
OptimizedVolume* vol = &om->volumes[volumeIdx];
|
||||
for(unsigned int faceIdx = 0; faceIdx < vol->faces.size(); faceIdx++)
|
||||
{
|
||||
OptimizedFace* face = &vol->faces[faceIdx];
|
||||
Point3 v0 = vol->points[face->index[0]].p;
|
||||
Point3 v1 = vol->points[face->index[1]].p;
|
||||
Point3 v2 = vol->points[face->index[2]].p;
|
||||
|
||||
Point3 normal = (v1 - v0).cross(v2 - v0);
|
||||
int32_t normalSize = normal.vSize();
|
||||
|
||||
double cosAngle = fabs(double(normal.z) / double(normalSize));
|
||||
|
||||
v0.x = (v0.x - storage.gridOffset.X) / storage.gridScale;
|
||||
v0.y = (v0.y - storage.gridOffset.Y) / storage.gridScale;
|
||||
v1.x = (v1.x - storage.gridOffset.X) / storage.gridScale;
|
||||
v1.y = (v1.y - storage.gridOffset.Y) / storage.gridScale;
|
||||
v2.x = (v2.x - storage.gridOffset.X) / storage.gridScale;
|
||||
v2.y = (v2.y - storage.gridOffset.Y) / storage.gridScale;
|
||||
|
||||
if (v0.x > v1.x) swap(v0, v1);
|
||||
if (v1.x > v2.x) swap(v1, v2);
|
||||
if (v0.x > v1.x) swap(v0, v1);
|
||||
for(int64_t x=v0.x; x<v1.x; x++)
|
||||
{
|
||||
int64_t y0 = v0.y + (v1.y - v0.y) * (x - v0.x) / (v1.x - v0.x);
|
||||
int64_t y1 = v0.y + (v2.y - v0.y) * (x - v0.x) / (v2.x - v0.x);
|
||||
int64_t z0 = v0.z + (v1.z - v0.z) * (x - v0.x) / (v1.x - v0.x);
|
||||
int64_t z1 = v0.z + (v2.z - v0.z) * (x - v0.x) / (v2.x - v0.x);
|
||||
|
||||
if (y0 > y1) { swap(y0, y1); swap(z0, z1); }
|
||||
for(int64_t y=y0; y<y1; y++)
|
||||
storage.grid[x+y*storage.gridWidth].push_back(SupportPoint(z0 + (z1 - z0) * (y-y0) / (y1-y0), cosAngle));
|
||||
}
|
||||
for(int64_t x=v1.x; x<v2.x; x++)
|
||||
{
|
||||
int64_t y0 = v1.y + (v2.y - v1.y) * (x - v1.x) / (v2.x - v1.x);
|
||||
int64_t y1 = v0.y + (v2.y - v0.y) * (x - v0.x) / (v2.x - v0.x);
|
||||
int64_t z0 = v1.z + (v2.z - v1.z) * (x - v1.x) / (v2.x - v1.x);
|
||||
int64_t z1 = v0.z + (v2.z - v0.z) * (x - v0.x) / (v2.x - v0.x);
|
||||
|
||||
if (y0 > y1) { swap(y0, y1); swap(z0, z1); }
|
||||
for(int64_t y=y0; y<y1; y++)
|
||||
storage.grid[x+y*storage.gridWidth].push_back(SupportPoint(z0 + (z1 - z0) * (y-y0) / (y1-y0), cosAngle));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(int32_t x=0; x<storage.gridWidth; x++)
|
||||
{
|
||||
for(int32_t y=0; y<storage.gridHeight; y++)
|
||||
{
|
||||
unsigned int n = x+y*storage.gridWidth;
|
||||
qsort(storage.grid[n].data(), storage.grid[n].size(), sizeof(SupportPoint), cmp_SupportPoint);
|
||||
}
|
||||
}
|
||||
storage.gridOffset.X += storage.gridScale / 2;
|
||||
storage.gridOffset.Y += storage.gridScale / 2;
|
||||
}
|
||||
|
||||
bool SupportPolyGenerator::needSupportAt(Point p)
|
||||
{
|
||||
if (p.X < 1) return false;
|
||||
if (p.Y < 1) return false;
|
||||
if (p.X >= storage.gridWidth - 1) return false;
|
||||
if (p.Y >= storage.gridHeight - 1) return false;
|
||||
if (done[p.X + p.Y * storage.gridWidth]) return false;
|
||||
|
||||
unsigned int n = p.X+p.Y*storage.gridWidth;
|
||||
|
||||
if (everywhere)
|
||||
{
|
||||
bool ok = false;
|
||||
for(unsigned int i=0; i<storage.grid[n].size(); i+=2)
|
||||
{
|
||||
if (storage.grid[n][i].cosAngle >= cosAngle && storage.grid[n][i].z - supportZDistance >= z && (i == 0 || storage.grid[n][i-1].z + supportZDistance < z))
|
||||
{
|
||||
ok = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!ok) return false;
|
||||
}else{
|
||||
if (storage.grid[n].size() < 1) return false;
|
||||
if (storage.grid[n][0].cosAngle < cosAngle) return false;
|
||||
if (storage.grid[n][0].z - supportZDistance < z) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void SupportPolyGenerator::lazyFill(Point startPoint)
|
||||
{
|
||||
static int nr = 0;
|
||||
nr++;
|
||||
PolygonRef poly = polygons.newPoly();
|
||||
Polygon tmpPoly;
|
||||
|
||||
while(1)
|
||||
{
|
||||
Point p = startPoint;
|
||||
done[p.X + p.Y * storage.gridWidth] = nr;
|
||||
while(needSupportAt(p + Point(1, 0)))
|
||||
{
|
||||
p.X ++;
|
||||
done[p.X + p.Y * storage.gridWidth] = nr;
|
||||
}
|
||||
tmpPoly.add(startPoint * storage.gridScale + storage.gridOffset - Point(storage.gridScale/2, 0));
|
||||
poly.add(p * storage.gridScale + storage.gridOffset);
|
||||
startPoint.Y++;
|
||||
while(!needSupportAt(startPoint) && startPoint.X <= p.X)
|
||||
startPoint.X ++;
|
||||
if (startPoint.X > p.X)
|
||||
{
|
||||
for(unsigned int n=0;n<tmpPoly.size();n++)
|
||||
{
|
||||
poly.add(tmpPoly[tmpPoly.size()-n-1]);
|
||||
}
|
||||
polygons.add(poly);
|
||||
return;
|
||||
}
|
||||
while(needSupportAt(startPoint - Point(1, 0)) && startPoint.X > 1)
|
||||
startPoint.X --;
|
||||
}
|
||||
}
|
||||
|
||||
SupportPolyGenerator::SupportPolyGenerator(SupportStorage& storage, int32_t z)
|
||||
: storage(storage), z(z), everywhere(storage.everywhere)
|
||||
{
|
||||
if (!storage.generated)
|
||||
if (support_type == Support_None)
|
||||
return;
|
||||
|
||||
cosAngle = cos(double(90 - storage.angle) / 180.0 * M_PI) - 0.01;
|
||||
this->supportZDistance = storage.ZDistance;
|
||||
|
||||
done = new int[storage.gridWidth*storage.gridHeight];
|
||||
memset(done, 0, sizeof(int) * storage.gridWidth*storage.gridHeight);
|
||||
double supportAngle = object->settings->getSettingInAngleRadians("support_angle");
|
||||
bool supportOnBuildplateOnly = support_type == Support_PlatformOnly;
|
||||
int supportZDistance = object->settings->getSettingInMicrons("support_z_distance");
|
||||
int supportZDistanceBottom = object->settings->getSettingInMicrons("support_bottom_distance");
|
||||
int supportZDistanceTop = object->settings->getSettingInMicrons("support_top_distance");
|
||||
int supportJoinDistance = object->settings->getSettingInMicrons("support_join_distance");
|
||||
int support_bottom_stair_step_height = object->settings->getSettingInMicrons("support_bottom_stair_step_height");
|
||||
int smoothing_distance = object->settings->getSettingInMicrons("support_area_smoothing");
|
||||
|
||||
for(int32_t y=1; y<storage.gridHeight; y++)
|
||||
int supportTowerDiameter = object->settings->getSettingInMicrons("support_tower_diameter");
|
||||
int supportMinAreaSqrt = object->settings->getSettingInMicrons("support_minimal_diameter");
|
||||
double supportTowerRoofAngle = object->settings->getSettingInAngleRadians("support_tower_roof_angle");
|
||||
|
||||
//std::cerr <<" towerDiameter=" << towerDiameter <<", supportMinAreaSqrt=" << supportMinAreaSqrt << std::endl;
|
||||
|
||||
int min_smoothing_area = 100*100; // minimal area for which to perform smoothing
|
||||
int z_layer_distance_tower = 1; // start tower directly below overhang point
|
||||
|
||||
int layerThickness = object->settings->getSettingInMicrons("layer_height");
|
||||
int extrusionWidth = object->settings->getSettingInMicrons("wall_line_width_x"); // TODO check for layer0extrusionWidth!
|
||||
int supportXYDistance = object->settings->getSettingInMicrons("support_xy_distance") + extrusionWidth / 2;
|
||||
|
||||
|
||||
|
||||
// derived settings:
|
||||
|
||||
if (supportZDistanceBottom < 0) supportZDistanceBottom = supportZDistance;
|
||||
if (supportZDistanceTop < 0) supportZDistanceTop = supportZDistance;
|
||||
|
||||
|
||||
int supportLayerThickness = layerThickness;
|
||||
|
||||
int layerZdistanceTop = supportZDistanceTop / supportLayerThickness + 1; // support must always be 1 layer below overhang
|
||||
int layerZdistanceBottom = supportZDistanceBottom / supportLayerThickness;
|
||||
|
||||
double tanAngle = tan(supportAngle) - 0.01; // the XY-component of the supportAngle
|
||||
int maxDistFromLowerLayer = tanAngle * supportLayerThickness; // max dist which can be bridged
|
||||
|
||||
int support_layer_count = layer_count;
|
||||
|
||||
double tanTowerRoofAngle = tan(supportTowerRoofAngle);
|
||||
int towerRoofExpansionDistance = layerThickness / tanTowerRoofAngle;
|
||||
|
||||
|
||||
// early out
|
||||
|
||||
if ( layerZdistanceTop + 1 > (int) support_layer_count )
|
||||
{
|
||||
for(int32_t x=1; x<storage.gridWidth; x++)
|
||||
storage.support.generated = false; // no (first layer) support can be generated
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// computation
|
||||
|
||||
|
||||
std::vector<Polygons> joinedLayers; // join model layers of all meshes into polygons and store small areas which need tower support
|
||||
std::vector<std::pair<int, std::vector<Polygons>>> overhang_points; // stores overhang_points along with the layer index at which the overhang point occurs
|
||||
AreaSupport::joinMeshesAndDetectOverhangPoints(storage, joinedLayers, overhang_points, layer_count, supportMinAreaSqrt, extrusionWidth);
|
||||
|
||||
|
||||
// initialization of supportAreasPerLayer
|
||||
for (int layer_idx = 0; layer_idx < layer_count ; layer_idx++)
|
||||
storage.support.supportAreasPerLayer.emplace_back();
|
||||
|
||||
|
||||
int overhang_points_pos = overhang_points.size() - 1;
|
||||
Polygons supportLayer_last;
|
||||
std::vector<Polygons> towerRoofs;
|
||||
for (int layer_idx = support_layer_count - 1 - layerZdistanceTop; layer_idx >= 0 ; layer_idx--)
|
||||
{
|
||||
|
||||
|
||||
// compute basic overhang and put in right layer ([layerZdistanceTOp] layers below)
|
||||
Polygons supportLayer_supportee = joinedLayers[layer_idx+layerZdistanceTop];
|
||||
Polygons supportLayer_supported = joinedLayers[layer_idx-1+layerZdistanceTop].offset(maxDistFromLowerLayer);
|
||||
Polygons basic_overhang = supportLayer_supportee.difference(supportLayer_supported);
|
||||
|
||||
Polygons support_extension = basic_overhang.offset(maxDistFromLowerLayer);
|
||||
support_extension = support_extension.intersection(supportLayer_supported);
|
||||
support_extension = support_extension.intersection(supportLayer_supportee);
|
||||
|
||||
Polygons overhang = basic_overhang.unionPolygons(support_extension);
|
||||
|
||||
/* supported
|
||||
* .................
|
||||
* ______________|
|
||||
* _______| ^^^^^ basic overhang
|
||||
*
|
||||
* ^^^^^^^^^ overhang extensions
|
||||
* ^^^^^^^^^^^^^^ overhang
|
||||
*/
|
||||
|
||||
|
||||
Polygons& supportLayer_this = overhang;
|
||||
|
||||
supportLayer_this = supportLayer_this.simplify(50); // TODO: hardcoded value!
|
||||
|
||||
if (supportMinAreaSqrt > 0)
|
||||
{
|
||||
if (!needSupportAt(Point(x, y)) || done[x + y * storage.gridWidth]) continue;
|
||||
// handle straight walls
|
||||
AreaSupport::handleWallStruts(supportLayer_this, supportMinAreaSqrt, supportTowerDiameter);
|
||||
// handle towers
|
||||
AreaSupport::handleTowers(supportLayer_this, towerRoofs, overhang_points, overhang_points_pos, layer_idx, towerRoofExpansionDistance, supportTowerDiameter, supportMinAreaSqrt, layer_count, z_layer_distance_tower);
|
||||
}
|
||||
|
||||
|
||||
if (layer_idx+1 < support_layer_count)
|
||||
{ // join with support from layer up
|
||||
Polygons& supportLayer_up = supportLayer_last;
|
||||
|
||||
lazyFill(Point(x, y));
|
||||
Polygons joined = supportLayer_this.unionPolygons(supportLayer_up);
|
||||
// join different parts
|
||||
if (supportJoinDistance > 0)
|
||||
{
|
||||
joined = joined.offset(supportJoinDistance);
|
||||
joined = joined.offset(-supportJoinDistance);
|
||||
}
|
||||
if (smoothing_distance > 0)
|
||||
joined = joined.smooth(smoothing_distance, min_smoothing_area);
|
||||
|
||||
// remove layer
|
||||
Polygons insetted = joined.difference(joinedLayers[layer_idx]);
|
||||
supportLayer_this = insetted;
|
||||
|
||||
}
|
||||
|
||||
|
||||
supportLayer_last = supportLayer_this;
|
||||
|
||||
// inset using X/Y distance
|
||||
if (supportLayer_this.size() > 0)
|
||||
supportLayer_this = supportLayer_this.difference(joinedLayers[layer_idx].offset(supportXYDistance));
|
||||
|
||||
// move up from model
|
||||
if (layerZdistanceBottom > 0 && layer_idx >= layerZdistanceBottom)
|
||||
{
|
||||
int stepHeight = support_bottom_stair_step_height / supportLayerThickness + 1;
|
||||
int bottomLayer = ((layer_idx - layerZdistanceBottom) / stepHeight) * stepHeight;
|
||||
supportLayer_this = supportLayer_this.difference(joinedLayers[bottomLayer]);
|
||||
}
|
||||
|
||||
storage.support.supportAreasPerLayer[layer_idx] = supportLayer_this;
|
||||
|
||||
logProgress("support", support_layer_count - layer_idx, support_layer_count);
|
||||
}
|
||||
|
||||
// do stuff for when support on buildplate only
|
||||
if (supportOnBuildplateOnly)
|
||||
{
|
||||
Polygons touching_buildplate = storage.support.supportAreasPerLayer[0];
|
||||
for (unsigned int layer_idx = 1 ; layer_idx < storage.support.supportAreasPerLayer.size() ; layer_idx++)
|
||||
{
|
||||
Polygons& supportLayer = storage.support.supportAreasPerLayer[layer_idx];
|
||||
|
||||
touching_buildplate = supportLayer.intersection(touching_buildplate); // from bottom to top, support areas can only decrease!
|
||||
|
||||
storage.support.supportAreasPerLayer[layer_idx] = touching_buildplate;
|
||||
}
|
||||
}
|
||||
|
||||
delete[] done;
|
||||
|
||||
polygons = polygons.offset(storage.XYDistance);
|
||||
storage.support.generated = true;
|
||||
}
|
||||
|
||||
void AreaSupport::joinMeshesAndDetectOverhangPoints(
|
||||
SliceDataStorage& storage,
|
||||
std::vector<Polygons>& joinedLayers,
|
||||
std::vector<std::pair<int, std::vector<Polygons>>>& overhang_points, // stores overhang_points along with the layer index at which the overhang point occurs)
|
||||
int layer_count,
|
||||
int supportMinAreaSqrt,
|
||||
int extrusionWidth
|
||||
)
|
||||
{
|
||||
for (int layer_idx = 0 ; layer_idx < layer_count ; layer_idx++)
|
||||
{
|
||||
joinedLayers.emplace_back();
|
||||
for (SliceMeshStorage& mesh : storage.meshes)
|
||||
{
|
||||
SliceLayer& layer = mesh.layers[layer_idx];
|
||||
for (SliceLayerPart& part : layer.parts)
|
||||
{
|
||||
|
||||
if (part.outline[0].area() < supportMinAreaSqrt * supportMinAreaSqrt)
|
||||
{
|
||||
Polygons part_poly = part.outline.offset(-extrusionWidth/2);
|
||||
if (part_poly.size() > 0)
|
||||
{
|
||||
if (overhang_points.size() > 0 && overhang_points.back().first == layer_idx)
|
||||
overhang_points.back().second.push_back(part_poly);
|
||||
else
|
||||
{
|
||||
std::vector<Polygons> small_part_polys;
|
||||
small_part_polys.push_back(part_poly);
|
||||
overhang_points.emplace_back<std::pair<int, std::vector<Polygons>>>(std::make_pair(layer_idx, small_part_polys));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
joinedLayers.back() = joinedLayers.back().unionPolygons(part.outline);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AreaSupport::handleTowers(
|
||||
Polygons& supportLayer_this,
|
||||
std::vector<Polygons>& towerRoofs,
|
||||
std::vector<std::pair<int, std::vector<Polygons>>>& overhang_points,
|
||||
int& overhang_points_pos,
|
||||
int layer_idx,
|
||||
int towerRoofExpansionDistance,
|
||||
int supportTowerDiameter,
|
||||
int supportMinAreaSqrt,
|
||||
int layer_count,
|
||||
int z_layer_distance_tower
|
||||
)
|
||||
{
|
||||
// handle new tower roof tops
|
||||
int layer_overhang_point = layer_idx + z_layer_distance_tower;
|
||||
if (overhang_points_pos >= 0 && layer_overhang_point < layer_count &&
|
||||
overhang_points[overhang_points_pos].first == layer_overhang_point)
|
||||
{
|
||||
std::vector<Polygons>& overhang_points_here = overhang_points[overhang_points_pos].second;
|
||||
{ // make sure we have the lowest point (make polys empty if they have small parts below)
|
||||
if (overhang_points_pos > 0 && overhang_points[overhang_points_pos - 1].first == layer_overhang_point - 1)
|
||||
{
|
||||
std::vector<Polygons>& overhang_points_below = overhang_points[overhang_points_pos - 1].second;
|
||||
for (Polygons& poly_here : overhang_points_here)
|
||||
{
|
||||
for (Polygons& poly_below : overhang_points_below)
|
||||
{
|
||||
poly_here = poly_here.difference(poly_below.offset(supportMinAreaSqrt*2));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (Polygons& poly : overhang_points_here)
|
||||
if (poly.size() > 0)
|
||||
towerRoofs.push_back(poly);
|
||||
overhang_points_pos--;
|
||||
}
|
||||
|
||||
// make tower roofs
|
||||
//for (Polygons& tower_roof : towerRoofs)
|
||||
for (unsigned int r = 0; r < towerRoofs.size(); r++)
|
||||
{
|
||||
supportLayer_this = supportLayer_this.unionPolygons(towerRoofs[r]);
|
||||
|
||||
Polygons& tower_roof = towerRoofs[r];
|
||||
if (tower_roof.size() > 0 && tower_roof[0].area() < supportTowerDiameter * supportTowerDiameter)
|
||||
{
|
||||
towerRoofs[r] = tower_roof.offset(towerRoofExpansionDistance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AreaSupport::handleWallStruts(
|
||||
Polygons& supportLayer_this,
|
||||
int supportMinAreaSqrt,
|
||||
int supportTowerDiameter
|
||||
)
|
||||
{
|
||||
for (unsigned int p = 0; p < supportLayer_this.size(); p++)
|
||||
{
|
||||
PolygonRef poly = supportLayer_this[p];
|
||||
if (poly.size() < 6) // might be a single wall
|
||||
{
|
||||
PolygonRef poly = supportLayer_this[p];
|
||||
int best = -1;
|
||||
int best_length2 = -1;
|
||||
for (unsigned int i = 0; i < poly.size(); i++)
|
||||
{
|
||||
int length2 = vSize2(poly[i] - poly[(i+1) % poly.size()]);
|
||||
if (length2 > best_length2)
|
||||
{
|
||||
best = i;
|
||||
best_length2 = length2;
|
||||
}
|
||||
}
|
||||
|
||||
if (best_length2 < supportMinAreaSqrt * supportMinAreaSqrt)
|
||||
break; // this is a small area, not a wall!
|
||||
|
||||
|
||||
// an estimate of the width of the area
|
||||
int width = sqrt( poly.area() * poly.area() / best_length2 ); // sqrt (a^2 / l^2) instead of a / sqrt(l^2)
|
||||
|
||||
// add square tower (strut) in the middle of the wall
|
||||
if (width < supportMinAreaSqrt)
|
||||
{
|
||||
Point mid = (poly[best] + poly[(best+1) % poly.size()] ) / 2;
|
||||
Polygons struts;
|
||||
PolygonRef strut = struts.newPoly();
|
||||
strut.add(mid + Point( supportTowerDiameter/2, supportTowerDiameter/2));
|
||||
strut.add(mid + Point(-supportTowerDiameter/2, supportTowerDiameter/2));
|
||||
strut.add(mid + Point(-supportTowerDiameter/2, -supportTowerDiameter/2));
|
||||
strut.add(mid + Point( supportTowerDiameter/2, -supportTowerDiameter/2));
|
||||
supportLayer_this = supportLayer_this.unionPolygons(struts);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}//namespace cura
|
||||
|
||||
+69
-18
@@ -3,32 +3,83 @@
|
||||
#define SUPPORT_H
|
||||
|
||||
#include "sliceDataStorage.h"
|
||||
#include "optimizedModel.h"
|
||||
#include "modelFile/modelFile.h"
|
||||
|
||||
namespace cura {
|
||||
|
||||
void generateSupportGrid(SupportStorage& storage, OptimizedModel* om, int supportAngle, bool supportEverywhere, int supportXYDistance, int supportZDistance);
|
||||
|
||||
class SupportPolyGenerator
|
||||
{
|
||||
class AreaSupport {
|
||||
public:
|
||||
Polygons polygons;
|
||||
|
||||
private:
|
||||
SupportStorage& storage;
|
||||
double cosAngle;
|
||||
int32_t z;
|
||||
int supportZDistance;
|
||||
bool everywhere;
|
||||
int* done;
|
||||
|
||||
bool needSupportAt(Point p);
|
||||
void lazyFill(Point startPoint);
|
||||
/*!
|
||||
* Joins the layerpart outlines of all meshes and collects the overhang points (small areas).
|
||||
* \param storage input layer outline information
|
||||
* \param overhang_points stores overhang_points along with the layer index at which the overhang point occurs
|
||||
* \param layer_count total number of layers
|
||||
* \param supportMinAreaSqrt diameter of the minimal area which can be supported without a specialized strut
|
||||
* \param extrusionWidth extrusionWidth
|
||||
*/
|
||||
static void joinMeshesAndDetectOverhangPoints(
|
||||
SliceDataStorage& storage,
|
||||
std::vector<Polygons>& joinedLayers,
|
||||
std::vector<std::pair<int, std::vector<Polygons>>>& overhang_points,
|
||||
int layer_count,
|
||||
int supportMinAreaSqrt,
|
||||
int extrusionWidth
|
||||
);
|
||||
|
||||
public:
|
||||
SupportPolyGenerator(SupportStorage& storage, int32_t z);
|
||||
/*!
|
||||
* Adds tower pieces to the current support layer.
|
||||
* From below the roof, the towers are added to the normal support layer and handled as normal support area.
|
||||
* \param supportLayer_this The support areas in the layer for which we are creating towers/struts
|
||||
* \param towerRoofs The parts of roofs which need to expand downward until they have the required diameter
|
||||
* \param overhang_points stores overhang_points along with the layer index at which the overhang point occurs
|
||||
* \param overhang_points_pos Index into \p overhang_points for the overhang points in the next layer
|
||||
* \param layer_idx The index of the layer at which to handle towers
|
||||
* \param towerRoofExpansionDistance The offset distance which determines the angle of the tower roof tops
|
||||
* \param supportTowerDiameter The diameter of the eventual tower, below the roof
|
||||
* \param supportMinAreaSqrt diameter of the minimal area which can be supported without a specialized strut
|
||||
* \param layer_count total number of layers
|
||||
* \param z_layer_distance_tower The number of layers between an overhang point and the top of a support tower
|
||||
*/
|
||||
static void handleTowers(
|
||||
Polygons& supportLayer_this,
|
||||
std::vector<Polygons>& towerRoofs,
|
||||
std::vector<std::pair<int, std::vector<Polygons>>>& overhang_points,
|
||||
int& overhang_points_pos,
|
||||
int layer_idx,
|
||||
int towerRoofExpansionDistance,
|
||||
int supportTowerDiameter,
|
||||
int supportMinAreaSqrt,
|
||||
int layer_count,
|
||||
int z_layer_distance_tower
|
||||
);
|
||||
|
||||
/*!
|
||||
* Adds struts (towers against a wall) to the current layer.
|
||||
* \param supportLayer_this The areas of the layer for which to handle the wall struts.
|
||||
* \param supportMinAreaSqrt The minimal diameter of a wall which doesn't need a strut for reinforcement
|
||||
* \param suportTowerDiameter The diameter of the strut
|
||||
*/
|
||||
static void handleWallStruts(
|
||||
Polygons& supportLayer_this,
|
||||
int supportMinAreaSqrt,
|
||||
int supportTowerDiameter
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
/*!
|
||||
* Generate support polygons over all layers.
|
||||
*
|
||||
* This function also handles small overhang areas (creates towers with larger diameter than just the overhang area) and single walls which could otherwise fall over.
|
||||
*
|
||||
* \param storage data storage containing the input layer outline data and containing the output support storage per layer
|
||||
* \param object The object for which to generate support areas
|
||||
* \param layer_count total number of layers
|
||||
*/
|
||||
void generateSupportAreas(SliceDataStorage& storage, SliceMeshStorage* object, int layer_count);
|
||||
|
||||
|
||||
}//namespace cura
|
||||
|
||||
#endif//SUPPORT_H
|
||||
|
||||
+312
@@ -0,0 +1,312 @@
|
||||
/*
|
||||
* The contnts of this file may be overwritten at any time!
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <cstdlib> // rand
|
||||
|
||||
|
||||
|
||||
#include "utils/polygonUtils.h"
|
||||
|
||||
using namespace cura;
|
||||
|
||||
/*
|
||||
#include "utils/intpoint.h"
|
||||
#include "utils/polygon.h"
|
||||
// Test whether polygon.inside(point) returns correct results.
|
||||
void test_poly_inside_and_centerOfMass()
|
||||
{
|
||||
{
|
||||
Polygon poly;
|
||||
poly.add(Point(2000,2000)); // /
|
||||
poly.add(Point(1000,1000)); // / /
|
||||
poly.add(Point(1100,100)); // |/
|
||||
|
||||
assert (!poly.inside(Point(-2000,1000)));
|
||||
assert (poly.inside(Point(1010,1000)));
|
||||
assert (!poly.inside(Point(5000,1000)));
|
||||
assert (poly.inside(Point(1111,1100)));
|
||||
assert (!poly.inside(Point(2001,2001)));
|
||||
assert (poly.inside(Point(1999,1998)));
|
||||
|
||||
std::cerr << "poly.centerOfMass() = " << poly.centerOfMass() << std::endl;
|
||||
Point center = poly.centerOfMass();
|
||||
for (int i = 0 ; i < 1000; i++)
|
||||
{
|
||||
Point translation(rand()%4000 - 2000, rand()%4000 - 2000);
|
||||
Polygon translated;
|
||||
for (Point& p : poly)
|
||||
{
|
||||
translated.add(p + translation);
|
||||
}
|
||||
Point translated_center = center + translation;
|
||||
if (vSize2(translated.centerOfMass() - translated_center) > 5*5)
|
||||
{
|
||||
std::cerr << "ERROR! test failed! " << std::endl;
|
||||
|
||||
std::cerr << "translated.centerOfMass() = " << translated.centerOfMass() << std::endl;
|
||||
std::cerr << "translated_center = " << translated_center << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
Polygon poly;
|
||||
poly.add(Point(0,0));
|
||||
poly.add(Point(100,0)); //
|
||||
poly.add(Point(100,100)); // |\ /|
|
||||
poly.add(Point(50,50)); // | \/ |
|
||||
poly.add(Point(0,100)); // |____|
|
||||
|
||||
assert (poly.inside(Point(60,50)));
|
||||
assert (!poly.inside(Point(50,60)));
|
||||
assert (poly.inside(Point(60,40)));
|
||||
assert (poly.inside(Point(50,40)));
|
||||
assert (!poly.inside(Point(-1,100)));
|
||||
assert (!poly.inside(Point(-10,-10)));
|
||||
|
||||
std::cerr << "poly.centerOfMass() = " << poly.centerOfMass() << std::endl;
|
||||
Point center = poly.centerOfMass();
|
||||
for (int i = 0 ; i < 1000; i++)
|
||||
{
|
||||
Point translation(rand()%4000 - 2000, rand()%4000 - 2000);
|
||||
Polygon translated;
|
||||
for (Point& p : poly)
|
||||
{
|
||||
translated.add(p + translation);
|
||||
}
|
||||
Point translated_center = center + translation;
|
||||
if (vSize2(translated.centerOfMass() - translated_center) > 5*5)
|
||||
{
|
||||
std::cerr << "ERROR! test failed! " << std::endl;
|
||||
|
||||
std::cerr << "translated.centerOfMass() = " << translated.centerOfMass() << std::endl;
|
||||
std::cerr << "translated_center = " << translated_center << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
Polygon poly;
|
||||
poly.add(Point( 0,2000)); // |\ .
|
||||
poly.add(Point( 0, 0)); // | > .
|
||||
poly.add(Point(1000,1000)); // |/
|
||||
|
||||
assert (poly.inside(Point(500,1000)));
|
||||
assert (poly.inside(Point(200,500)));
|
||||
assert (poly.inside(Point(200,1500)));
|
||||
assert (poly.inside(Point(800,1000)));
|
||||
assert (!poly.inside(Point(-10,1000)));
|
||||
assert (!poly.inside(Point(1100,1000)));
|
||||
assert (!poly.inside(Point(600,500)));
|
||||
assert (!poly.inside(Point(600,1500)));
|
||||
assert (!poly.inside(Point(2000,1000)));
|
||||
|
||||
std::cerr << "poly.centerOfMass() = " << poly.centerOfMass() << std::endl;
|
||||
Point center = poly.centerOfMass();
|
||||
for (int i = 0 ; i < 1000; i++)
|
||||
{
|
||||
Point translation(rand()%4000 - 2000, rand()%4000 - 2000);
|
||||
Polygon translated;
|
||||
for (Point& p : poly)
|
||||
{
|
||||
translated.add(translation - Point(-p.X, p.Y));
|
||||
}
|
||||
Point translated_center = translation - Point(-center.X, center.Y);
|
||||
if (vSize2(translated.centerOfMass() - translated_center) > 5*5)
|
||||
{
|
||||
std::cerr << "ERROR! test failed! " << std::endl;
|
||||
std::cerr << "translated.centerOfMass() = " << translated.centerOfMass() << std::endl;
|
||||
std::cerr << "translated_center = " << translated_center << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
/*
|
||||
struct LocationItem
|
||||
{
|
||||
Point p;
|
||||
int i;
|
||||
LocationItem(Point p, int i) : p(p), i(i) {};
|
||||
LocationItem() : p(0,0), i(-1) {};
|
||||
};
|
||||
|
||||
#include "utils/BucketGrid2D.h"
|
||||
void test_BucketGrid2D()
|
||||
{
|
||||
|
||||
BucketGrid2D<LocationItem> bg(1000);
|
||||
for (int i = 0; i < 20000; i++)
|
||||
{
|
||||
Point p(rand()%100000, rand()%100000);
|
||||
LocationItem li(p, i);
|
||||
bg.insert(p, li);
|
||||
}
|
||||
// {Point p(00,00); int i = 1; bg.insert(p, i);}
|
||||
// {Point p(05,05); int i = 2; bg.insert(p, i);}
|
||||
// {Point p(14,15); int i = 3; bg.insert(p, i);}
|
||||
// {Point p(25,25); int i = 4; bg.insert(p, i);}
|
||||
// {Point p(39,39); int i = 5; bg.insert(p, i);}
|
||||
// {Point p(300,300); int i = 6; bg.insert(p, i);}
|
||||
|
||||
Point to(rand()%100000,rand()%100000);
|
||||
std::cerr << to << std::endl;
|
||||
LocationItem result;
|
||||
if (bg.findNearestObject(to, result))
|
||||
{
|
||||
std::cerr << "best: " << result.p << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "nothing found!" << std::endl;
|
||||
}
|
||||
//bg.debug();
|
||||
}*/
|
||||
|
||||
/*
|
||||
#include <math.h>
|
||||
#include "utils/gettime.h"
|
||||
void test_findClosestConnection()
|
||||
{
|
||||
srand(1234);
|
||||
if (false)
|
||||
{
|
||||
Polygon poly2;
|
||||
poly2.add(Point(0,300));
|
||||
poly2.add(Point(100,300)); // ____
|
||||
poly2.add(Point(100,200)); // | |
|
||||
poly2.add(Point(50,250)); // | /\ |
|
||||
poly2.add(Point(0,200)); // |/ \|
|
||||
|
||||
Polygon poly1;
|
||||
poly1.add(Point(0,0));
|
||||
poly1.add(Point(100,0)); //
|
||||
poly1.add(Point(100,100)); // |\ /|
|
||||
poly1.add(Point(50,50)); // | \/ |
|
||||
poly1.add(Point(0,100)); // |____|
|
||||
|
||||
ClosestPolygonPoint result1 (poly1);
|
||||
ClosestPolygonPoint result2 (poly2);
|
||||
|
||||
findSmallestConnection(result1, result2, 3);
|
||||
std::cerr << result1.location << " -- " << result2.location << std::endl;
|
||||
}
|
||||
|
||||
if (false)
|
||||
{
|
||||
Polygon poly2;
|
||||
poly2.add(Point(0,300));
|
||||
poly2.add(Point(100,300)); // ____
|
||||
poly2.add(Point(100,200)); // | |
|
||||
poly2.add(Point(50,250)); // | /\ |
|
||||
poly2.add(Point(10,105)); // |/ \|
|
||||
|
||||
Polygon poly1;
|
||||
poly1.add(Point(0,0));
|
||||
poly1.add(Point(100,0)); //
|
||||
poly1.add(Point(100,100)); // |\ /|
|
||||
poly1.add(Point(50,50)); // | \/ |
|
||||
poly1.add(Point(0,100)); // |____|
|
||||
|
||||
ClosestPolygonPoint result1 (poly1);
|
||||
ClosestPolygonPoint result2 (poly2);
|
||||
|
||||
findSmallestConnection(result1, result2, 3);
|
||||
std::cerr << result1.location << " -- " << result2.location << std::endl;
|
||||
}
|
||||
|
||||
double creationTime = 0;
|
||||
double evalTime = 0;
|
||||
long totalLength = 0;
|
||||
TimeKeeper timer;
|
||||
for (int i = 0; i < 10000; i++)
|
||||
{ // for vizualization as csv with e.g. Rstudio
|
||||
Polygon poly1;
|
||||
double dist = 100;
|
||||
for (double a = 0; a < 360; a += 1)
|
||||
{
|
||||
dist += int(rand()%3) -1;
|
||||
Point p(static_cast<int>(dist * std::cos(a/180.0*3.1415)), static_cast<int>(dist * std::sin(a/180.0*3.1415)));
|
||||
p = p + Point(0, 200);
|
||||
if ( a ==0)
|
||||
poly1.add(p);
|
||||
else
|
||||
poly1.add((poly1.back() + p) / 2);
|
||||
// std::cerr << poly1.back().X << ", " << poly1.back().Y << std::endl;
|
||||
}
|
||||
// std::cerr << " " << std::endl;
|
||||
Polygon poly2;
|
||||
dist = 100;
|
||||
for (double a = 0; a < 360; a += 1)
|
||||
{
|
||||
|
||||
dist += int(rand()%3) - 1;
|
||||
Point p(static_cast<int>(dist * std::cos(a/180.0*3.1415)), static_cast<int>(dist * std::sin(a/180.0*3.1415)));
|
||||
if ( a ==0)
|
||||
poly2.add(p);
|
||||
else
|
||||
poly2.add((poly2.back() + p) / 2);
|
||||
// std::cerr << poly2.back().X << ", " << poly2.back().Y << std::endl;
|
||||
}
|
||||
creationTime += timer.restart();
|
||||
ClosestPolygonPoint result1 (poly1);
|
||||
ClosestPolygonPoint result2 (poly2);
|
||||
|
||||
findSmallestConnection(result1, result2, 240);
|
||||
totalLength += vSize(result1.location - result2.location);
|
||||
evalTime += timer.restart();
|
||||
// std::cerr << " " << std::endl;
|
||||
// std::cerr << result1.location.X << " , " << result1.location.Y << std::endl;
|
||||
// std::cerr << result2.location.X << " , " << result2.location.Y << std::endl;
|
||||
// std::cerr << " " << std::endl;
|
||||
}
|
||||
|
||||
std::cerr << "creationTime : " << creationTime << std::endl;
|
||||
std::cerr << "evalTime : " << evalTime << std::endl;
|
||||
std::cerr << "totalLength : " << totalLength << std::endl;
|
||||
}
|
||||
*/
|
||||
|
||||
void test_clipper()
|
||||
{
|
||||
Polygon p;
|
||||
p.emplace_back(0, 11004);
|
||||
p.emplace_back(0, 10129);
|
||||
p.emplace_back(0, 9185);
|
||||
p.emplace_back(0, 8477);
|
||||
p.emplace_back(1, 8491);
|
||||
p.emplace_back(418, 8861);
|
||||
p.emplace_back(1080, 9389);
|
||||
p.emplace_back(2106, 10142);
|
||||
p.emplace_back(3000, 10757);
|
||||
p.emplace_back(3000, 12010);
|
||||
p.emplace_back(3000, 12790);
|
||||
p.emplace_back(3000, 13485);
|
||||
p.emplace_back(3000, 14088);
|
||||
p.emplace_back(3000, 14601);
|
||||
p.emplace_back(3000, 15354);
|
||||
p.emplace_back(3000, 24867);
|
||||
p.emplace_back(3000, 25469);
|
||||
p.emplace_back(3000, 26303);
|
||||
p.emplace_back(3000, 27421);
|
||||
p.emplace_back(3000, 28242);
|
||||
p.emplace_back(2107, 28856);
|
||||
p.emplace_back(1080, 29610);
|
||||
p.emplace_back(608, 29986);
|
||||
p.emplace_back(1, 30508);
|
||||
p.emplace_back(1, 30522);
|
||||
p.emplace_back(0, 11772);
|
||||
|
||||
Polygons polys;
|
||||
polys.add(p);
|
||||
|
||||
polys.debugOutputHTML("output/problem_test.html", true);
|
||||
polys.offset(-400).debugOutputHTML("output/problem_test_offset.html", true);
|
||||
polys = polys.removeDegenerateVerts();
|
||||
polys.offset(-400).debugOutputHTML("output/problem_test_offset_solved.html", true);
|
||||
}
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
// test_findClosestConnection();
|
||||
test_clipper();
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
/** Copyright (C) 2015 Tim Kuipers- Released under terms of the AGPLv3 License */
|
||||
#ifndef BUCKET_GRID_2D_H
|
||||
#define BUCKET_GRID_2D_H
|
||||
|
||||
#include "logoutput.h"
|
||||
#include "intpoint.h"
|
||||
#include <unordered_map>
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
/*!
|
||||
* Container for items with location for which the lookup for nearby items is optimized.
|
||||
*
|
||||
* It functions by hashing the items location and lookuping up based on the hash of that location and the hashes of nearby locations.
|
||||
*/
|
||||
template<typename T>
|
||||
class BucketGrid2D
|
||||
{
|
||||
private:
|
||||
|
||||
/*!
|
||||
* Returns a point for which the hash is at a grid position of \p relativeHash relative to \p p.
|
||||
*
|
||||
* \param p The point for which to get the relative point to hash
|
||||
* \param relativeHash The relative position - in grid terms - of the relative point.
|
||||
* \return A point for which the hash is at a grid position of \p relativeHash relative to \p p.
|
||||
*/
|
||||
inline Point getRelativeForHash(const Point& p, const Point& relativeHash)
|
||||
{
|
||||
return p + relativeHash*squareSize;
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
* A hash class representing the hash function object.
|
||||
*/
|
||||
struct PointHasher
|
||||
{
|
||||
|
||||
/*!
|
||||
* The basic hash function for a 2D grid position.
|
||||
* \param p The grid location to hash
|
||||
* \return the hash
|
||||
*/
|
||||
inline uint32_t pointHash_simple(const Point& p) const
|
||||
{
|
||||
return p.X ^ (p.Y << 8);
|
||||
//return (p.X / 20000) ^ (p.Y / 20000) << 8;
|
||||
}
|
||||
|
||||
/*!
|
||||
* The hash function for a 2D position.
|
||||
* \param point The location to hash
|
||||
* \return the hash
|
||||
*/
|
||||
inline uint32_t pointHash(const Point& point) const
|
||||
{
|
||||
Point p = point/squareSize;
|
||||
return pointHash_simple(p);
|
||||
}
|
||||
/*
|
||||
inline uint32_t pointHash(const Point& point, const Point& relativeHash) const
|
||||
{
|
||||
Point p = p/squareSize + relativeHash;
|
||||
return pointHash_simple(p);
|
||||
}*/
|
||||
|
||||
/*!
|
||||
* The horizontal and vertical size of a cell in the grid; the width and height of a bucket.
|
||||
*/
|
||||
int squareSize;
|
||||
|
||||
/*!
|
||||
* Basic constructor.
|
||||
* \param squareSize The horizontal and vertical size of a cell in the grid; the width and height of a bucket.
|
||||
*/
|
||||
PointHasher(int squareSize) : squareSize(squareSize) {};
|
||||
|
||||
/*!
|
||||
* See PointHasher::pointHash
|
||||
*/
|
||||
uint32_t operator()(const Point& p) const { return pointHash(p); };
|
||||
|
||||
};
|
||||
|
||||
/*!
|
||||
* A helper predicate object which allways returns false when comparing two objects.
|
||||
*
|
||||
* This is used for mapping each point to a unique object, even when two objects have the same point associated with it.
|
||||
*/
|
||||
struct NeverEqual
|
||||
{
|
||||
template<typename S>
|
||||
bool operator()(const S& p1, const S& p2) const { return false; };
|
||||
};
|
||||
|
||||
|
||||
private:
|
||||
/*!
|
||||
* Basic constructor.
|
||||
* \param squareSize The horizontal and vertical size of a cell in the grid; the width and height of a bucket.
|
||||
*/
|
||||
int squareSize;
|
||||
/*!
|
||||
* The map type used to associate points with their objects.
|
||||
*/
|
||||
typedef typename std::unordered_map<Point, T, PointHasher, NeverEqual> Map;
|
||||
|
||||
/*!
|
||||
* The map used to associate points with their objects.
|
||||
*/
|
||||
Map point2object;
|
||||
|
||||
|
||||
public:
|
||||
/*!
|
||||
* The constructor for a bucket grid.
|
||||
*
|
||||
* \param squareSize The horizontal and vertical size of a cell in the grid; the width and height of a bucket.
|
||||
* \param initial_map_size The minimal number of initial buckets
|
||||
*/
|
||||
BucketGrid2D(int squareSize, unsigned int initial_map_size = 4) : squareSize(squareSize), point2object(initial_map_size, PointHasher(squareSize)) {};
|
||||
|
||||
/*!
|
||||
* Find all objects with a point in a grid cell at a distance of one cell from the cell of \p p.
|
||||
*
|
||||
* \warning Objects may occur multiple times in the output!
|
||||
*
|
||||
* \param p The point for which to find close points.
|
||||
* \param ret Ouput parameter: all objects close to \p p.
|
||||
*/
|
||||
void findNearbyObjects(Point& p, std::vector<T>& ret)
|
||||
{
|
||||
for (int x = -1; x <= 1; x++)
|
||||
{
|
||||
for (int y = -1; y <= 1; y++)
|
||||
{
|
||||
int bucket_idx = point2object.bucket(getRelativeForHash(p, Point(x,y))); // when the hash is not a hash of a present item, the bucket_idx returned may be one already encountered
|
||||
for ( auto local_it = point2object.begin(bucket_idx); local_it!= point2object.end(bucket_idx); ++local_it )
|
||||
{
|
||||
ret.push_back(local_it->second);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/*!
|
||||
* Find all objects with a point in a grid cell at a distance of one cell from the cell of \p p.
|
||||
*
|
||||
* \warning Objects may occur multiple times in the output!
|
||||
*
|
||||
* \param p The point for which to find close points.
|
||||
* \return All objects close to \p p.
|
||||
*/
|
||||
std::vector<T> findNearbyObjects(Point& p)
|
||||
{
|
||||
std::vector<T> ret;
|
||||
findNearbyObjects(p, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Find the nearest object to a given lcoation \p p, if there is any in a neighboring cell in the grid.
|
||||
*
|
||||
* \param p The point for which to find the nearest object.
|
||||
* \param nearby Output parameter: the nearest object, if any
|
||||
* \return Whether an object has been found.
|
||||
*/
|
||||
bool findNearestObject(Point& p, T& nearby)
|
||||
{
|
||||
bool found = false;
|
||||
int64_t bestDist2 = squareSize*9; // 9 > sqrt(2*2 + 2*2)^2 which is the square of the largest distance of a point to a point in a neighboring cell
|
||||
for (int x = -1; x <= 1; x++)
|
||||
{
|
||||
for (int y = -1; y <= 1; y++)
|
||||
{
|
||||
int bucket_idx = point2object.bucket(getRelativeForHash(p, Point(x,y)));
|
||||
for ( auto local_it = point2object.begin(bucket_idx); local_it!= point2object.end(bucket_idx); ++local_it )
|
||||
{
|
||||
int32_t dist2 = vSize2(local_it->first - p);
|
||||
if (dist2 < bestDist2)
|
||||
{
|
||||
found = true;
|
||||
nearby = local_it->second;
|
||||
bestDist2 = dist2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return found;
|
||||
};
|
||||
|
||||
|
||||
/*!
|
||||
* Insert a new point into the bucket grid.
|
||||
*
|
||||
* \param p The location associated with \p t.
|
||||
* \param t The object to insert in the grid cell for position \p p.
|
||||
*/
|
||||
void insert(Point& p, T& t)
|
||||
{
|
||||
typedef typename Map::iterator iter;
|
||||
std::pair<iter, bool> emplaced = point2object.emplace(p, t);
|
||||
// if (! emplaced.second)
|
||||
// logError("Error! BucketGrid2D couldn't insert object!");
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
} // namespace cura
|
||||
#endif//BUCKET_GRID_2D_H
|
||||
+119
-77
@@ -1,83 +1,125 @@
|
||||
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
|
||||
#ifndef FLOAT_POINT_H
|
||||
#define FLOAT_POINT_H
|
||||
|
||||
/*
|
||||
Floating point 3D points are used during model loading as 3D vectors.
|
||||
They represent millimeters in 3D space.
|
||||
*/
|
||||
|
||||
#include "intpoint.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <math.h>
|
||||
|
||||
class FPoint3
|
||||
{
|
||||
public:
|
||||
double x,y,z;
|
||||
FPoint3() {}
|
||||
FPoint3(double _x, double _y, double _z): x(_x), y(_y), z(_z) {}
|
||||
|
||||
FPoint3 operator+(const FPoint3& p) const { return FPoint3(x+p.x, y+p.y, z+p.z); }
|
||||
FPoint3 operator-(const FPoint3& p) const { return FPoint3(x-p.x, y-p.y, z-p.z); }
|
||||
FPoint3 operator*(const double f) const { return FPoint3(x*f, y*f, z*f); }
|
||||
FPoint3 operator/(const double f) const { return FPoint3(x/f, y/f, z/f); }
|
||||
|
||||
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
|
||||
#ifndef FLOAT_POINT_H
|
||||
#define FLOAT_POINT_H
|
||||
|
||||
/*
|
||||
Floating point 3D points are used during model loading as 3D vectors.
|
||||
They represent millimeters in 3D space.
|
||||
*/
|
||||
|
||||
#include "intpoint.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <math.h>
|
||||
|
||||
class FPoint3
|
||||
{
|
||||
public:
|
||||
float x,y,z;
|
||||
FPoint3() {}
|
||||
FPoint3(float _x, float _y, float _z): x(_x), y(_y), z(_z) {}
|
||||
FPoint3(const Point3& p): x(p.x*.001), y(p.y*.001), z(p.z*.001) {}
|
||||
|
||||
FPoint3 operator+(const FPoint3& p) const { return FPoint3(x+p.x, y+p.y, z+p.z); }
|
||||
FPoint3 operator-(const FPoint3& p) const { return FPoint3(x-p.x, y-p.y, z-p.z); }
|
||||
FPoint3 operator*(const float f) const { return FPoint3(x*f, y*f, z*f); }
|
||||
FPoint3 operator/(const float f) const { return FPoint3(x/f, y/f, z/f); }
|
||||
|
||||
FPoint3& operator += (const FPoint3& p) { x += p.x; y += p.y; z += p.z; return *this; }
|
||||
FPoint3& operator -= (const FPoint3& p) { x -= p.x; y -= p.y; z -= p.z; return *this; }
|
||||
|
||||
bool operator==(FPoint3& p) const { return x==p.x&&y==p.y&&z==p.z; }
|
||||
bool operator!=(FPoint3& p) const { return x!=p.x||y!=p.y||z!=p.z; }
|
||||
|
||||
double max()
|
||||
{
|
||||
if (x > y && x > z) return x;
|
||||
if (y > z) return y;
|
||||
return z;
|
||||
FPoint3& operator -= (const FPoint3& p) { x -= p.x; y -= p.y; z -= p.z; return *this; }
|
||||
FPoint3& operator *= (const float f) { x *= f; y *= f; z *= f; return *this; }
|
||||
|
||||
bool operator==(FPoint3& p) const { return x==p.x&&y==p.y&&z==p.z; }
|
||||
bool operator!=(FPoint3& p) const { return x!=p.x||y!=p.y||z!=p.z; }
|
||||
|
||||
float max()
|
||||
{
|
||||
if (x > y && x > z) return x;
|
||||
if (y > z) return y;
|
||||
return z;
|
||||
}
|
||||
|
||||
bool testLength(float len)
|
||||
{
|
||||
return vSize2() <= len*len;
|
||||
}
|
||||
|
||||
float vSize2()
|
||||
{
|
||||
return x*x+y*y+z*z;
|
||||
}
|
||||
|
||||
float vSize()
|
||||
{
|
||||
return sqrt(vSize2());
|
||||
}
|
||||
|
||||
bool testLength(double len)
|
||||
{
|
||||
return vSize2() <= len*len;
|
||||
}
|
||||
|
||||
double vSize2()
|
||||
{
|
||||
return x*x+y*y+z*z;
|
||||
}
|
||||
|
||||
double vSize()
|
||||
{
|
||||
return sqrt(vSize2());
|
||||
}
|
||||
};
|
||||
|
||||
class FMatrix3x3
|
||||
{
|
||||
public:
|
||||
double m[3][3];
|
||||
|
||||
FMatrix3x3()
|
||||
inline FPoint3 normalized()
|
||||
{
|
||||
m[0][0] = 1.0;
|
||||
m[1][0] = 0.0;
|
||||
m[2][0] = 0.0;
|
||||
m[0][1] = 0.0;
|
||||
m[1][1] = 1.0;
|
||||
m[2][1] = 0.0;
|
||||
m[0][2] = 0.0;
|
||||
m[1][2] = 0.0;
|
||||
m[2][2] = 1.0;
|
||||
return (*this)/vSize();
|
||||
}
|
||||
|
||||
Point3 apply(FPoint3 p)
|
||||
{
|
||||
return Point3(
|
||||
MM2INT(p.x * m[0][0] + p.y * m[1][0] + p.z * m[2][0]),
|
||||
MM2INT(p.x * m[0][1] + p.y * m[1][1] + p.z * m[2][1]),
|
||||
MM2INT(p.x * m[0][2] + p.y * m[1][2] + p.z * m[2][2]));
|
||||
}
|
||||
};
|
||||
|
||||
#endif//INT_POINT_H
|
||||
FPoint3 cross(const FPoint3& p)
|
||||
{
|
||||
return FPoint3(
|
||||
y*p.z-z*p.y,
|
||||
z*p.x-x*p.z,
|
||||
x*p.y-y*p.x);
|
||||
}
|
||||
|
||||
static FPoint3 cross(const Point3& a, const Point3& b)
|
||||
{
|
||||
return FPoint3(a).cross(FPoint3(b));
|
||||
// FPoint3(
|
||||
// a.y*b.z-a.z*b.y,
|
||||
// a.z*b.x-a.x*b.z,
|
||||
// a.x*b.y-a.y*b.x);
|
||||
}
|
||||
|
||||
Point3 toPoint3()
|
||||
{
|
||||
return Point3(x*1000, y*1000, z*1000);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//inline FPoint3 operator+(FPoint3 lhs, const FPoint3& rhs) {
|
||||
// lhs += rhs;
|
||||
// return lhs;
|
||||
//}
|
||||
inline float operator*(FPoint3 lhs, const FPoint3& rhs) {
|
||||
return lhs.x*rhs.x + lhs.y*rhs.y + lhs.z*rhs.z;
|
||||
}
|
||||
//inline FPoint3 operator*(FPoint3 lhs, const float f) {
|
||||
// lhs *= f;
|
||||
// return lhs;
|
||||
//}
|
||||
|
||||
class FMatrix3x3
|
||||
{
|
||||
public:
|
||||
double m[3][3];
|
||||
|
||||
FMatrix3x3()
|
||||
{
|
||||
m[0][0] = 1.0;
|
||||
m[1][0] = 0.0;
|
||||
m[2][0] = 0.0;
|
||||
m[0][1] = 0.0;
|
||||
m[1][1] = 1.0;
|
||||
m[2][1] = 0.0;
|
||||
m[0][2] = 0.0;
|
||||
m[1][2] = 0.0;
|
||||
m[2][2] = 1.0;
|
||||
}
|
||||
|
||||
Point3 apply(const FPoint3& p)
|
||||
{
|
||||
return Point3(
|
||||
MM2INT(p.x * m[0][0] + p.y * m[1][0] + p.z * m[2][0]),
|
||||
MM2INT(p.x * m[0][1] + p.y * m[1][1] + p.z * m[2][1]),
|
||||
MM2INT(p.x * m[0][2] + p.y * m[1][2] + p.z * m[2][2]));
|
||||
}
|
||||
};
|
||||
|
||||
#endif//INT_POINT_H
|
||||
|
||||
+77
-11
@@ -2,7 +2,7 @@
|
||||
#ifndef INT_POINT_H
|
||||
#define INT_POINT_H
|
||||
|
||||
/*
|
||||
/**
|
||||
The integer point classes are used as soon as possible and represent microns in 2D or 3D space.
|
||||
Integer points are used to avoid floating point rounding errors, and because ClipperLib uses them.
|
||||
*/
|
||||
@@ -15,6 +15,8 @@ Integer points are used to avoid floating point rounding errors, and because Cli
|
||||
#include <stdint.h>
|
||||
#include <cmath>
|
||||
|
||||
#include <iostream> // auto-serialization / auto-toString()
|
||||
|
||||
#define INT2MM(n) (double(n) / 1000.0)
|
||||
#define MM2INT(n) (int64_t((n) * 1000))
|
||||
|
||||
@@ -26,6 +28,15 @@ Integer points are used to avoid floating point rounding errors, and because Cli
|
||||
#define M_PI 3.14159265358979323846
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define DEPRECATED(func) func __attribute__ ((deprecated))
|
||||
#elif defined(_MSC_VER)
|
||||
#define DEPRECATED(func) __declspec(deprecated) func
|
||||
#else
|
||||
#pragma message("WARNING: You need to implement DEPRECATED for this compiler")
|
||||
#define DEPRECATED(func) func
|
||||
#endif
|
||||
|
||||
class Point3
|
||||
{
|
||||
public:
|
||||
@@ -36,6 +47,8 @@ public:
|
||||
Point3 operator+(const Point3& p) const { return Point3(x+p.x, y+p.y, z+p.z); }
|
||||
Point3 operator-(const Point3& p) const { return Point3(x-p.x, y-p.y, z-p.z); }
|
||||
Point3 operator/(const int32_t i) const { return Point3(x/i, y/i, z/i); }
|
||||
Point3 operator*(const int32_t i) const { return Point3(x*i, y*i, z*i); }
|
||||
Point3 operator*(const double d) const { return Point3(d*x, d*y, d*z); }
|
||||
|
||||
Point3& operator += (const Point3& p) { x += p.x; y += p.y; z += p.z; return *this; }
|
||||
Point3& operator -= (const Point3& p) { x -= p.x; y -= p.y; z -= p.z; return *this; }
|
||||
@@ -43,6 +56,16 @@ public:
|
||||
bool operator==(const Point3& p) const { return x==p.x&&y==p.y&&z==p.z; }
|
||||
bool operator!=(const Point3& p) const { return x!=p.x||y!=p.y||z!=p.z; }
|
||||
|
||||
|
||||
template<class CharT, class TraitsT>
|
||||
friend
|
||||
std::basic_ostream<CharT, TraitsT>&
|
||||
operator <<(std::basic_ostream<CharT, TraitsT>& os, const Point3& p)
|
||||
{
|
||||
return os << "(" << p.x << ", " << p.y << ", " << p.z << ")";
|
||||
}
|
||||
|
||||
|
||||
int32_t max()
|
||||
{
|
||||
if (x > y && x > z) return x;
|
||||
@@ -61,27 +84,50 @@ public:
|
||||
return vSize2() <= len*len;
|
||||
}
|
||||
|
||||
int64_t vSize2()
|
||||
int64_t vSize2() const
|
||||
{
|
||||
return int64_t(x)*int64_t(x)+int64_t(y)*int64_t(y)+int64_t(z)*int64_t(z);
|
||||
}
|
||||
|
||||
int32_t vSize()
|
||||
int32_t vSize() const
|
||||
{
|
||||
return sqrt(vSize2());
|
||||
}
|
||||
|
||||
Point3 cross(const Point3& p)
|
||||
|
||||
double vSizeMM() const
|
||||
{
|
||||
double fx = INT2MM(x);
|
||||
double fy = INT2MM(y);
|
||||
double fz = INT2MM(z);
|
||||
return sqrt(fx*fx+fy*fy+fz*fz);
|
||||
}
|
||||
/*! this function is deprecated because it can cause overflows for vectors which easily fit inside a printer. Use FPoint3.cross(a,b) instead. */
|
||||
DEPRECATED(Point3 cross(const Point3& p))
|
||||
{
|
||||
return Point3(
|
||||
y*p.z-z*p.y,
|
||||
z*p.x-x*p.z,
|
||||
y*p.z-z*p.y, /// dangerous for vectors longer than 4.6 cm !!!!!
|
||||
z*p.x-x*p.z, /// can cause overflows
|
||||
x*p.y-y*p.x);
|
||||
}
|
||||
|
||||
int64_t dot(const Point3& p)
|
||||
{
|
||||
return x*p.x + y*p.y + z*p.z;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
inline Point3 operator*(const int32_t i, const Point3& rhs) {
|
||||
return rhs * i;
|
||||
}
|
||||
|
||||
inline Point3 operator*(const double d, const Point3& rhs) {
|
||||
return rhs * d;
|
||||
}
|
||||
|
||||
/* 64bit Points are used mostly troughout the code, these are the 2D points from ClipperLib */
|
||||
typedef ClipperLib::IntPoint Point;
|
||||
|
||||
class IntPoint {
|
||||
public:
|
||||
int X, Y;
|
||||
@@ -91,16 +137,18 @@ public:
|
||||
#define POINT_MAX std::numeric_limits<ClipperLib::cInt>::max()
|
||||
|
||||
/* Extra operators to make it easier to do math with the 64bit Point objects */
|
||||
INLINE Point operator-(const Point& p0) { return Point(-p0.X, -p0.Y); }
|
||||
INLINE Point operator+(const Point& p0, const Point& p1) { return Point(p0.X+p1.X, p0.Y+p1.Y); }
|
||||
INLINE Point operator-(const Point& p0, const Point& p1) { return Point(p0.X-p1.X, p0.Y-p1.Y); }
|
||||
INLINE Point operator*(const Point& p0, const int32_t i) { return Point(p0.X*i, p0.Y*i); }
|
||||
INLINE Point operator*(const int32_t i, const Point& p0) { return p0 * i; }
|
||||
INLINE Point operator/(const Point& p0, const int32_t i) { return Point(p0.X/i, p0.Y/i); }
|
||||
|
||||
//Point& operator += (const Point& p) { x += p.x; y += p.y; return *this; }
|
||||
//Point& operator -= (const Point& p) { x -= p.x; y -= p.y; return *this; }
|
||||
INLINE Point& operator += (Point& p0, const Point& p1) { p0.X += p1.X; p0.Y += p1.Y; return p0; }
|
||||
INLINE Point& operator -= (Point& p0, const Point& p1) { p0.X -= p1.X; p0.Y -= p1.Y; return p0; }
|
||||
|
||||
INLINE bool operator==(const Point& p0, const Point& p1) { return p0.X==p1.X&&p0.Y==p1.Y; }
|
||||
INLINE bool operator!=(const Point& p0, const Point& p1) { return p0.X!=p1.X||p0.Y!=p1.Y; }
|
||||
//INLINE bool operator==(const Point& p0, const Point& p1) { return p0.X==p1.X&&p0.Y==p1.Y; }
|
||||
//INLINE bool operator!=(const Point& p0, const Point& p1) { return p0.X!=p1.X||p0.Y!=p1.Y; }
|
||||
|
||||
INLINE int64_t vSize2(const Point& p0)
|
||||
{
|
||||
@@ -200,4 +248,22 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
inline Point3 operator+(const Point3& p3, const Point& p2) {
|
||||
return Point3(p3.x + p2.X, p3.y + p2.Y, p3.z);
|
||||
}
|
||||
|
||||
inline Point operator+(const Point& p2, const Point3& p3) {
|
||||
return Point(p3.x + p2.X, p3.y + p2.Y);
|
||||
}
|
||||
|
||||
|
||||
inline Point3 operator-(const Point3& p3, const Point& p2) {
|
||||
return Point3(p3.x - p2.X, p3.y - p2.Y, p3.z);
|
||||
}
|
||||
|
||||
inline Point operator-(const Point& p2, const Point3& p3) {
|
||||
return Point(p2.X - p3.x, p2.Y - p3.y);
|
||||
}
|
||||
|
||||
#endif//INT_POINT_H
|
||||
|
||||
@@ -20,6 +20,15 @@ void enableProgressLogging()
|
||||
}
|
||||
|
||||
void logError(const char* fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vfprintf(stderr, fmt, args);
|
||||
va_end(args);
|
||||
fflush(stderr);
|
||||
}
|
||||
|
||||
void logCopyright(const char* fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
|
||||
@@ -11,6 +11,8 @@ void enableProgressLogging();
|
||||
void logError(const char* fmt, ...);
|
||||
//Report a message if the verbose level is 1 or higher. (defined as _log to prevent clash with log() function from <math.h>)
|
||||
void log(const char* fmt, ...);
|
||||
//Report an copyright message (always reported, independed of verbose level)
|
||||
void logCopyright(const char* fmt, ...);
|
||||
|
||||
//Report engine progress to interface if any. Only if "enableProgressLogging()" has been called.
|
||||
void logProgress(const char* type, int value, int maxValue);
|
||||
|
||||
+467
-27
@@ -4,9 +4,11 @@
|
||||
#include <vector>
|
||||
#include <assert.h>
|
||||
#include <float.h>
|
||||
using std::vector;
|
||||
#include <clipper/clipper.hpp>
|
||||
|
||||
#include <algorithm> // std::reverse
|
||||
#include <cmath> // fabs
|
||||
|
||||
#include "intpoint.h"
|
||||
|
||||
//#define CHECK_POLY_ACCESS
|
||||
@@ -18,6 +20,18 @@ using std::vector;
|
||||
|
||||
namespace cura {
|
||||
|
||||
enum PolygonType
|
||||
{
|
||||
NoneType,
|
||||
Inset0Type,
|
||||
InsetXType,
|
||||
SkinType,
|
||||
SupportType,
|
||||
SkirtType,
|
||||
InfillType,
|
||||
SupportInfillType
|
||||
};
|
||||
|
||||
const static int clipper_init = (0);
|
||||
#define NO_INDEX (std::numeric_limits<unsigned int>::max())
|
||||
|
||||
@@ -37,7 +51,7 @@ public:
|
||||
return polygon->size();
|
||||
}
|
||||
|
||||
Point operator[] (unsigned int index) const
|
||||
Point& operator[] (unsigned int index) const
|
||||
{
|
||||
POLY_ASSERT(index < size());
|
||||
return (*polygon)[index];
|
||||
@@ -52,6 +66,12 @@ public:
|
||||
{
|
||||
polygon->push_back(p);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void emplace_back(Args... args)
|
||||
{
|
||||
polygon->emplace_back(args...);
|
||||
}
|
||||
|
||||
void remove(unsigned int index)
|
||||
{
|
||||
@@ -86,11 +106,47 @@ public:
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
Point min() const
|
||||
{
|
||||
Point ret = Point(POINT_MAX, POINT_MAX);
|
||||
for(Point p : *polygon)
|
||||
{
|
||||
ret.X = std::min(ret.X, p.X);
|
||||
ret.Y = std::min(ret.Y, p.Y);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
Point max() const
|
||||
{
|
||||
Point ret = Point(POINT_MIN, POINT_MIN);
|
||||
for(Point p : *polygon)
|
||||
{
|
||||
ret.X = std::max(ret.X, p.X);
|
||||
ret.Y = std::max(ret.Y, p.Y);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
double area() const
|
||||
{
|
||||
return ClipperLib::Area(*polygon);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Translate the whole polygon in some direction.
|
||||
*
|
||||
* \param translation The direction in which to move the polygon
|
||||
*/
|
||||
void translate(Point translation)
|
||||
{
|
||||
for (Point& p : *this)
|
||||
{
|
||||
p += translation;
|
||||
}
|
||||
}
|
||||
|
||||
Point centerOfMass() const
|
||||
{
|
||||
@@ -107,14 +163,10 @@ public:
|
||||
}
|
||||
|
||||
double area = Area(*polygon);
|
||||
|
||||
x = x / 6 / area;
|
||||
y = y / 6 / area;
|
||||
|
||||
if (x < 0)
|
||||
{
|
||||
x = -x;
|
||||
y = -y;
|
||||
}
|
||||
return Point(x, y);
|
||||
}
|
||||
|
||||
@@ -134,30 +186,180 @@ public:
|
||||
return ret;
|
||||
}
|
||||
|
||||
//Check if we are inside the polygon. We do this by tracing from the point towards the negative X direction,
|
||||
// every line we cross increments the crossings counter. If we have an even number of crossings then we are not inside the polygon.
|
||||
bool inside(Point p)
|
||||
/*!
|
||||
* Check if we are inside the polygon. We do this by tracing from the point towards the positive X direction,
|
||||
* every line we cross increments the crossings counter. If we have an even number of crossings then we are not inside the polygon.
|
||||
* Care needs to be taken, if p.Y exactly matches a vertex to the right of p, then we need to count 1 intersect if the
|
||||
* outline passes vertically past; and 0 (or 2) intersections if that point on the outline is a 'top' or 'bottom' vertex.
|
||||
* The easiest way to do this is to break out two cases for increasing and decreasing Y ( from p0 to p1 ).
|
||||
* A segment is tested if pa.Y <= p.Y < pb.Y, where pa and pb are the points (from p0,p1) with smallest & largest Y.
|
||||
* When both have the same Y, no intersections are counted but there is a special test to see if the point falls
|
||||
* exactly on the line.
|
||||
*
|
||||
* Returns false if outside, true if inside; if the point lies exactly on the border, will return 'border_result'.
|
||||
*
|
||||
* \param p The point for which to check if it is inside this polygon
|
||||
* \param border_result What to return when the point is exactly on the border
|
||||
* \return Whether the point \p p is inside this polygon (or \p border_result when it is on the border)
|
||||
*/
|
||||
bool inside(Point p, bool border_result=false)
|
||||
{
|
||||
if (polygon->size() < 1)
|
||||
PolygonRef thiss = *this;
|
||||
if (size() < 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int crossings = 0;
|
||||
Point p0 = (*polygon)[polygon->size()-1];
|
||||
for(unsigned int n=0; n<polygon->size(); n++)
|
||||
Point p0 = back();
|
||||
for(unsigned int n=0; n<size(); n++)
|
||||
{
|
||||
Point p1 = (*polygon)[n];
|
||||
|
||||
if ((p0.Y >= p.Y && p1.Y < p.Y) || (p1.Y > p.Y && p0.Y <= p.Y))
|
||||
Point p1 = thiss[n];
|
||||
// no tests unless the segment p0-p1 is at least partly at, or to right of, p.X
|
||||
if ( std::max(p0.X, p1.X) >= p.X )
|
||||
{
|
||||
int64_t x = p0.X + (p1.X - p0.X) * (p.Y - p0.Y) / (p1.Y - p0.Y);
|
||||
if (x >= p.X)
|
||||
crossings ++;
|
||||
int64_t pdY = p1.Y-p0.Y;
|
||||
if (pdY < 0) // p0->p1 is 'falling'
|
||||
{
|
||||
if ( p1.Y <= p.Y && p0.Y > p.Y ) // candidate
|
||||
{
|
||||
// dx > 0 if intersection is to right of p.X
|
||||
int64_t dx = (p1.X - p0.X) * (p1.Y - p.Y) - (p1.X-p.X)*pdY;
|
||||
if (dx == 0) // includes p == p1
|
||||
{
|
||||
return border_result;
|
||||
}
|
||||
if (dx > 0)
|
||||
{
|
||||
crossings ++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (p.Y >= p0.Y)
|
||||
{
|
||||
if (p.Y < p1.Y) // candidate for p0->p1 'rising' and includes p.Y
|
||||
{
|
||||
// dx > 0 if intersection is to right of p.X
|
||||
int64_t dx = (p1.X - p0.X) * (p.Y - p0.Y) - (p.X-p0.X)*pdY;
|
||||
if (dx == 0) // includes p == p0
|
||||
{
|
||||
return border_result;
|
||||
}
|
||||
if (dx > 0)
|
||||
{
|
||||
crossings ++;
|
||||
}
|
||||
}
|
||||
else if (p.Y == p1.Y)
|
||||
{
|
||||
// some special cases here, points on border:
|
||||
// - p1 exactly matches p (might otherwise be missed)
|
||||
// - p0->p1 exactly horizontal, and includes p.
|
||||
// (we already tested std::max(p0.X,p1.X) >= p.X )
|
||||
if (p.X == p1.X ||
|
||||
(pdY==0 && std::min(p0.X,p1.X) <= p.X) )
|
||||
{
|
||||
return border_result;
|
||||
// otherwise, count no crossings
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
p0 = p1;
|
||||
}
|
||||
return (crossings % 2) == 1;
|
||||
}
|
||||
|
||||
void smooth(int remove_length, PolygonRef result)
|
||||
{
|
||||
PolygonRef& thiss = *this;
|
||||
ClipperLib::Path* poly = result.polygon;
|
||||
if (size() > 0)
|
||||
poly->push_back(thiss[0]);
|
||||
for (unsigned int poly_idx = 1; poly_idx < size(); poly_idx++)
|
||||
{
|
||||
if (shorterThen(thiss[poly_idx-1]-thiss[poly_idx], remove_length))
|
||||
{
|
||||
poly_idx++; // skip the next line piece (dont escalate the removal of edges)
|
||||
if (poly_idx < size())
|
||||
poly->push_back(thiss[poly_idx]);
|
||||
} else poly->push_back(thiss[poly_idx]);
|
||||
}
|
||||
}
|
||||
|
||||
void simplify(int allowed_error_distance_squared, PolygonRef result) //!< removes consecutive line segments with same orientation
|
||||
{
|
||||
PolygonRef& thiss = *this;
|
||||
ClipperLib::Path* poly = result.polygon;
|
||||
|
||||
if (size() < 4)
|
||||
{
|
||||
for (unsigned int poly_idx = 0; poly_idx < size(); poly_idx++)
|
||||
poly->push_back(thiss[poly_idx]);
|
||||
return;
|
||||
}
|
||||
|
||||
Point& last = thiss[0];
|
||||
result.add(last);
|
||||
for (unsigned int poly_idx = 1; poly_idx < size(); poly_idx++)
|
||||
{
|
||||
/*
|
||||
* /|
|
||||
* c / | a
|
||||
* /__|
|
||||
* \ b|
|
||||
* e \ | d
|
||||
* \|
|
||||
*
|
||||
* b^2 = c^2 - a^2
|
||||
* b^2 = e^2 - d^2
|
||||
*
|
||||
* approximately: (this is asymptotically true for d -> 0)
|
||||
* a/d = c/e
|
||||
* a/(a+d) = c/(c+e)
|
||||
* a^2 / (a+d)^2 = c^2 / (c+e)^2
|
||||
* a^2 = c^2 * (a+d)^2/ (c+e)^2
|
||||
*
|
||||
*/
|
||||
if ( vSize2(thiss[poly_idx]-last) < allowed_error_distance_squared )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
Point& next = thiss[(poly_idx+1) % size()];
|
||||
auto square = [](double in) { return in*in; };
|
||||
int64_t a2 = vSize2(next-thiss[poly_idx]) * vSize2(next-last) / static_cast<int64_t>(square(vSizeMM(next-last) + vSizeMM(thiss[poly_idx]-last))*1000*1000);
|
||||
|
||||
int64_t error2 = vSize2(next-thiss[poly_idx]) - a2;
|
||||
if (error2 < allowed_error_distance_squared)
|
||||
{
|
||||
// don't add the point to the result
|
||||
} else
|
||||
{
|
||||
poly->push_back(thiss[poly_idx]);
|
||||
last = thiss[poly_idx];
|
||||
}
|
||||
}
|
||||
|
||||
if (result.size() < 3)
|
||||
{
|
||||
poly->clear();
|
||||
|
||||
for (unsigned int poly_idx = 0; poly_idx < size(); poly_idx++)
|
||||
poly->push_back(thiss[poly_idx]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void pop_back()
|
||||
{
|
||||
polygon->pop_back();
|
||||
}
|
||||
|
||||
ClipperLib::Path::reference back() const
|
||||
{
|
||||
return polygon->back();
|
||||
}
|
||||
|
||||
ClipperLib::Path::iterator begin()
|
||||
{
|
||||
return polygon->begin();
|
||||
@@ -203,7 +405,7 @@ class Polygons
|
||||
private:
|
||||
ClipperLib::Paths polygons;
|
||||
public:
|
||||
unsigned int size()
|
||||
unsigned int size() const
|
||||
{
|
||||
return polygons.size();
|
||||
}
|
||||
@@ -213,6 +415,14 @@ public:
|
||||
POLY_ASSERT(index < size());
|
||||
return PolygonRef(polygons[index]);
|
||||
}
|
||||
ClipperLib::Paths::iterator begin()
|
||||
{
|
||||
return polygons.begin();
|
||||
}
|
||||
ClipperLib::Paths::iterator end()
|
||||
{
|
||||
return polygons.end();
|
||||
}
|
||||
void remove(unsigned int index)
|
||||
{
|
||||
POLY_ASSERT(index < size());
|
||||
@@ -236,6 +446,10 @@ public:
|
||||
polygons.push_back(ClipperLib::Path());
|
||||
return PolygonRef(polygons[polygons.size()-1]);
|
||||
}
|
||||
PolygonRef back()
|
||||
{
|
||||
return polygons[polygons.size()-1];
|
||||
}
|
||||
|
||||
Polygons() {}
|
||||
Polygons(const Polygons& other) { polygons = other.polygons; }
|
||||
@@ -267,18 +481,68 @@ public:
|
||||
clipper.Execute(ClipperLib::ctIntersection, ret.polygons);
|
||||
return ret;
|
||||
}
|
||||
Polygons offset(int distance) const
|
||||
Polygons xorPolygons(const Polygons& other) const
|
||||
{
|
||||
Polygons ret;
|
||||
ClipperLib::ClipperOffset clipper;
|
||||
clipper.AddPaths(polygons, ClipperLib::jtMiter, ClipperLib::etClosedPolygon);
|
||||
clipper.MiterLimit = 2.0;
|
||||
ClipperLib::Clipper clipper(clipper_init);
|
||||
clipper.AddPaths(polygons, ClipperLib::ptSubject, true);
|
||||
clipper.AddPaths(other.polygons, ClipperLib::ptClip, true);
|
||||
clipper.Execute(ClipperLib::ctXor, ret.polygons);
|
||||
return ret;
|
||||
}
|
||||
Polygons offset(int distance, ClipperLib::JoinType joinType = ClipperLib::jtMiter) const
|
||||
{
|
||||
Polygons ret;
|
||||
double miterLimit = 1.2;
|
||||
ClipperLib::ClipperOffset clipper(miterLimit, 10.0);
|
||||
clipper.AddPaths(polygons, joinType, ClipperLib::etClosedPolygon);
|
||||
clipper.MiterLimit = miterLimit;
|
||||
clipper.Execute(ret.polygons, distance);
|
||||
return ret;
|
||||
}
|
||||
vector<Polygons> splitIntoParts(bool unionAll = false) const
|
||||
|
||||
Polygons smooth(int remove_length, int min_area) //!< removes points connected to small lines
|
||||
{
|
||||
vector<Polygons> ret;
|
||||
Polygons ret;
|
||||
for (unsigned int p = 0; p < size(); p++)
|
||||
{
|
||||
PolygonRef poly(polygons[p]);
|
||||
if (poly.area() < min_area || poly.size() <= 5) // when optimally removing, a poly with 5 pieces results in a triangle. Smaller polys dont have area!
|
||||
{
|
||||
ret.add(poly);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (poly.size() == 0)
|
||||
continue;
|
||||
if (poly.size() < 4)
|
||||
ret.add(poly);
|
||||
else
|
||||
poly.smooth(remove_length, ret.newPoly());
|
||||
|
||||
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
Polygons simplify(int allowed_error_distance) //!< removes points connected to similarly oriented lines
|
||||
{
|
||||
int allowed_error_distance_squared = allowed_error_distance * allowed_error_distance;
|
||||
Polygons ret;
|
||||
Polygons& thiss = *this;
|
||||
for (unsigned int p = 0; p < size(); p++)
|
||||
{
|
||||
thiss[p].simplify(allowed_error_distance_squared, ret.newPoly());
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
/*!
|
||||
* Split up the polygons into groups according to the even-odd rule.
|
||||
* Each polygons in the result has an outline as first polygon, whereas the rest are holes.
|
||||
*/
|
||||
std::vector<Polygons> splitIntoParts(bool unionAll = false) const
|
||||
{
|
||||
std::vector<Polygons> ret;
|
||||
ClipperLib::Clipper clipper(clipper_init);
|
||||
ClipperLib::PolyTree resultPolyTree;
|
||||
clipper.AddPaths(polygons, ClipperLib::ptSubject, true);
|
||||
@@ -290,8 +554,121 @@ public:
|
||||
_processPolyTreeNode(&resultPolyTree, ret);
|
||||
return ret;
|
||||
}
|
||||
/*!
|
||||
* Removes polygons with area smaller than \p minAreaSize (note that minAreaSize is in mm^2, not in micron^2).
|
||||
*/
|
||||
void removeSmallAreas(double minAreaSize)
|
||||
{
|
||||
Polygons& thiss = *this;
|
||||
for(unsigned int i=0; i<size(); i++)
|
||||
{
|
||||
double area = INT2MM(INT2MM(fabs(thiss[i].area())));
|
||||
if (area < minAreaSize) // Only create an up/down skin if the area is large enough. So you do not create tiny blobs of "trying to fill"
|
||||
{
|
||||
remove(i);
|
||||
i -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*!
|
||||
* Removes overlapping consecutive line segments which don't delimit a positive area.
|
||||
*/
|
||||
Polygons removeDegenerateVerts()
|
||||
{
|
||||
Polygons ret;
|
||||
for (PolygonRef poly : *this)
|
||||
{
|
||||
Polygon result;
|
||||
|
||||
auto isDegenerate = [](Point& last, Point& now, Point& next)
|
||||
{
|
||||
Point last_line = now - last;
|
||||
Point next_line = next - now;
|
||||
return dot(last_line, next_line) == -1 * vSize(last_line) * vSize(next_line);
|
||||
};
|
||||
|
||||
for (unsigned int idx = 0; idx < poly.size(); idx++)
|
||||
{
|
||||
Point& last = (result.size() == 0) ? poly.back() : result.back();
|
||||
if (idx+1 == poly.size() && result.size() == 0) { break; }
|
||||
Point& next = (idx+1 == poly.size())? result[0] : poly[idx+1];
|
||||
if ( isDegenerate(last, poly[idx], next) )
|
||||
{ // lines are in the opposite direction
|
||||
// don't add vert to the result
|
||||
while (result.size() > 1 && isDegenerate(result[result.size()-2], result.back(), next) )
|
||||
{
|
||||
result.pop_back();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result.add(poly[idx]);
|
||||
}
|
||||
}
|
||||
|
||||
if (result.size() > 2) { ret.add(result); }
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
/*!
|
||||
* Removes the same polygons from this set (and also empty polygons).
|
||||
* Polygons are considered the same if all points lie within [same_distance] of their counterparts.
|
||||
*/
|
||||
Polygons remove(Polygons& to_be_removed, int same_distance = 0)
|
||||
{
|
||||
Polygons result;
|
||||
for (unsigned int poly_keep_idx = 0; poly_keep_idx < size(); poly_keep_idx++)
|
||||
{
|
||||
PolygonRef poly_keep = (*this)[poly_keep_idx];
|
||||
bool should_be_removed = false;
|
||||
if (poly_keep.size() > 0)
|
||||
// for (int hole_poly_idx = 0; hole_poly_idx < to_be_removed.size(); hole_poly_idx++)
|
||||
for (PolygonRef poly_rem : to_be_removed)
|
||||
{
|
||||
// PolygonRef poly_rem = to_be_removed[hole_poly_idx];
|
||||
if (poly_rem.size() != poly_keep.size() || poly_rem.size() == 0) continue;
|
||||
|
||||
// find closest point, supposing this point aligns the two shapes in the best way
|
||||
int closest_point_idx = 0;
|
||||
int smallestDist2 = -1;
|
||||
for (unsigned int point_rem_idx = 0; point_rem_idx < poly_rem.size(); point_rem_idx++)
|
||||
{
|
||||
int dist2 = vSize2(poly_rem[point_rem_idx] - poly_keep[0]);
|
||||
if (dist2 < smallestDist2 || smallestDist2 < 0)
|
||||
{
|
||||
smallestDist2 = dist2;
|
||||
closest_point_idx = point_rem_idx;
|
||||
}
|
||||
}
|
||||
bool poly_rem_is_poly_keep = true;
|
||||
// compare the two polygons on all points
|
||||
if (smallestDist2 > same_distance * same_distance)
|
||||
continue;
|
||||
for (unsigned int point_idx = 0; point_idx < poly_rem.size(); point_idx++)
|
||||
{
|
||||
int dist2 = vSize2(poly_rem[(closest_point_idx + point_idx) % poly_rem.size()] - poly_keep[point_idx]);
|
||||
if (dist2 > same_distance * same_distance)
|
||||
{
|
||||
poly_rem_is_poly_keep = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (poly_rem_is_poly_keep)
|
||||
{
|
||||
should_be_removed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!should_be_removed)
|
||||
result.add(poly_keep);
|
||||
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
void _processPolyTreeNode(ClipperLib::PolyNode* node, vector<Polygons>& ret) const
|
||||
void _processPolyTreeNode(ClipperLib::PolyNode* node, std::vector<Polygons>& ret) const
|
||||
{
|
||||
for(int n=0; n<node->ChildCount(); n++)
|
||||
{
|
||||
@@ -331,6 +708,34 @@ public:
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
Point min() const
|
||||
{
|
||||
Point ret = Point(POINT_MAX, POINT_MAX);
|
||||
for(const ClipperLib::Path& polygon : polygons)
|
||||
{
|
||||
for(Point p : polygon)
|
||||
{
|
||||
ret.X = std::min(ret.X, p.X);
|
||||
ret.Y = std::min(ret.Y, p.Y);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
Point max() const
|
||||
{
|
||||
Point ret = Point(POINT_MIN, POINT_MIN);
|
||||
for(const ClipperLib::Path& polygon : polygons)
|
||||
{
|
||||
for(Point p : polygon)
|
||||
{
|
||||
ret.X = std::max(ret.X, p.X);
|
||||
ret.Y = std::max(ret.Y, p.Y);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool inside(Point p)
|
||||
{
|
||||
@@ -356,6 +761,41 @@ public:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void debugOutputHTML(const char* filename, bool dotTheVertices = false)
|
||||
{
|
||||
FILE* out = fopen(filename, "w");
|
||||
fprintf(out, "<!DOCTYPE html><html><body>");
|
||||
Point modelSize = max() - min();
|
||||
modelSize.X = std::max(modelSize.X, modelSize.Y);
|
||||
modelSize.Y = std::max(modelSize.X, modelSize.Y);
|
||||
Point modelMin = min();
|
||||
|
||||
fprintf(out, "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" style=\"width: 500px; height:500px\">\n");
|
||||
for(Polygons& parts : splitIntoParts())
|
||||
{
|
||||
for(unsigned int j=0;j<parts.size();j++)
|
||||
{
|
||||
Polygon poly = parts[j];
|
||||
fprintf(out, "<polygon points=\"");
|
||||
for(Point& p : poly)
|
||||
{
|
||||
fprintf(out, "%f,%f ", float(p.X - modelMin.X)/modelSize.X*500, float(p.Y - modelMin.Y)/modelSize.Y*500);
|
||||
}
|
||||
if (j == 0)
|
||||
fprintf(out, "\" style=\"fill:gray; stroke:black;stroke-width:1\" />\n");
|
||||
else
|
||||
fprintf(out, "\" style=\"fill:red; stroke:black;stroke-width:1\" />\n");
|
||||
|
||||
if (dotTheVertices)
|
||||
for(Point& p : poly)
|
||||
fprintf(out, "<circle cx=\"%f\" cy=\"%f\" r=\"2\" stroke=\"black\" stroke-width=\"3\" fill=\"black\" />", float(p.X - modelMin.X)/modelSize.X*500, float(p.Y - modelMin.Y)/modelSize.Y*500);
|
||||
}
|
||||
}
|
||||
fprintf(out, "</svg>\n");
|
||||
fprintf(out, "</body></html>");
|
||||
fclose(out);
|
||||
}
|
||||
};
|
||||
|
||||
/* Axis aligned boundary box */
|
||||
|
||||
@@ -0,0 +1,372 @@
|
||||
/** Copyright (C) 2015 Tim Kuipers - Released under terms of the AGPLv3 License */
|
||||
#include "polygonUtils.h"
|
||||
|
||||
#include <list>
|
||||
|
||||
#include "../debug.h"
|
||||
namespace cura
|
||||
{
|
||||
|
||||
void offsetExtrusionWidth(Polygons& poly, bool inward, int extrusionWidth, Polygons& result, Polygons* in_between, bool avoidOverlappingPerimeters)
|
||||
{
|
||||
int distance = (inward)? -extrusionWidth : extrusionWidth;
|
||||
if (!avoidOverlappingPerimeters)
|
||||
{
|
||||
result = poly.offset(distance);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = poly.offset(distance*3/2).offset(-distance/2); // overshoot by half the extrusionWidth
|
||||
if (in_between) // if a pointer for in_between is given
|
||||
in_between->add(poly.offset(distance/2).difference(result.offset(-distance/2)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void offsetSafe(Polygons& poly, int distance, int extrusionWidth, Polygons& result, bool avoidOverlappingPerimeters)
|
||||
{
|
||||
int direction = (distance > 0)? 1 : -1;
|
||||
if (!avoidOverlappingPerimeters)
|
||||
{
|
||||
result = poly.offset(distance);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = poly.offset(distance + direction*extrusionWidth/2).offset(-direction * extrusionWidth/2);
|
||||
}
|
||||
}
|
||||
|
||||
void removeOverlapping(Polygons& poly, int extrusionWidth, Polygons& result)
|
||||
{
|
||||
result = poly.offset(extrusionWidth/2).offset(-extrusionWidth).offset(extrusionWidth/2);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void findSmallestConnection(ClosestPolygonPoint& poly1_result, ClosestPolygonPoint& poly2_result, int sample_size)
|
||||
{
|
||||
PolygonRef poly1 = poly1_result.poly;
|
||||
PolygonRef poly2 = poly2_result.poly;
|
||||
if (poly1.size() == 0 || poly2.size() == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int bestDist2 = -1;
|
||||
|
||||
int step1 = std::max<unsigned int>(2, poly1.size() / sample_size);
|
||||
int step2 = std::max<unsigned int>(2, poly2.size() / sample_size);
|
||||
for (unsigned int i = 0; i < poly1.size(); i += step1)
|
||||
{
|
||||
for (unsigned int j = 0; j < poly2.size(); j += step2)
|
||||
{
|
||||
int dist2 = vSize2(poly1[i] - poly2[j]);
|
||||
if (bestDist2 == -1 || dist2 < bestDist2)
|
||||
{
|
||||
bestDist2 = dist2;
|
||||
poly1_result.pos = i;
|
||||
poly2_result.pos = j;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
walkToNearestSmallestConnection(poly1_result, poly2_result);
|
||||
}
|
||||
|
||||
void findSmallestConnection_OLD(ClosestPolygonPoint& poly1_result, ClosestPolygonPoint& poly2_result, int sample_size)
|
||||
{
|
||||
PolygonRef poly1 = poly1_result.poly;
|
||||
PolygonRef poly2 = poly2_result.poly;
|
||||
if (poly1.size() == 0 || poly2.size() == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int bestDist2 = -1;
|
||||
|
||||
int step1 = std::max<unsigned int>(2, poly1.size() / sample_size);
|
||||
int step2 = std::max<unsigned int>(2, poly2.size() / sample_size);
|
||||
for (unsigned int i = 0; i < poly1.size(); i += step1)
|
||||
{
|
||||
for (unsigned int j = 0; j < poly2.size(); j += step2)
|
||||
{
|
||||
ClosestPolygonPoint here1(i, poly1);
|
||||
ClosestPolygonPoint here2(j, poly2);
|
||||
walkToNearestSmallestConnection(here1, here2);
|
||||
|
||||
int dist2 = vSize2(here1.location - here2.location);
|
||||
if (bestDist2 == -1 || dist2 < bestDist2)
|
||||
{
|
||||
bestDist2 = dist2;
|
||||
poly1_result = here1;
|
||||
poly2_result = here2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void walkToNearestSmallestConnection(ClosestPolygonPoint& poly1_result, ClosestPolygonPoint& poly2_result)
|
||||
{
|
||||
PolygonRef poly1 = poly1_result.poly;
|
||||
PolygonRef poly2 = poly2_result.poly;
|
||||
if (poly1_result.pos < 0 || poly2_result.pos < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int equilibirum_limit = 100; // hard coded value
|
||||
for (int loop_counter = 0; loop_counter < equilibirum_limit; loop_counter++)
|
||||
{
|
||||
int pos1_before = poly1_result.pos;
|
||||
poly1_result = findNearestClosest(poly2_result.location, poly1, poly1_result.pos);
|
||||
int pos2_before = poly2_result.pos;
|
||||
poly2_result = findNearestClosest(poly1_result.location, poly2, poly2_result.pos);
|
||||
|
||||
if (poly1_result.pos == pos1_before && poly2_result.pos == pos2_before)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ClosestPolygonPoint findNearestClosest(Point from, PolygonRef polygon, int start_idx)
|
||||
{
|
||||
ClosestPolygonPoint forth = findNearestClosest(from, polygon, start_idx, 1);
|
||||
ClosestPolygonPoint back = findNearestClosest(from, polygon, start_idx, -1);
|
||||
if (vSize2(forth.location - from) < vSize2(back.location - from))
|
||||
{
|
||||
return forth;
|
||||
}
|
||||
else
|
||||
{
|
||||
return back;
|
||||
}
|
||||
}
|
||||
|
||||
ClosestPolygonPoint findNearestClosest(Point from, PolygonRef polygon, int start_idx, int direction)
|
||||
{
|
||||
if (polygon.size() == 0)
|
||||
{
|
||||
return ClosestPolygonPoint(polygon);
|
||||
}
|
||||
Point aPoint = polygon[0];
|
||||
Point best = aPoint;
|
||||
|
||||
int64_t closestDist = vSize2(from - best);
|
||||
int bestPos = 0;
|
||||
|
||||
for (unsigned int p = 0; p<polygon.size(); p++)
|
||||
{
|
||||
int p1_idx = (polygon.size() + direction*p + start_idx) % polygon.size();
|
||||
int p2_idx = (polygon.size() + direction*(p+1) + start_idx) % polygon.size();
|
||||
Point& p1 = polygon[p1_idx];
|
||||
Point& p2 = polygon[p2_idx];
|
||||
|
||||
Point closestHere = getClosestOnLine(from, p1 ,p2);
|
||||
int64_t dist = vSize2(from - closestHere);
|
||||
if (dist < closestDist)
|
||||
{
|
||||
best = closestHere;
|
||||
closestDist = dist;
|
||||
bestPos = p1_idx;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ClosestPolygonPoint(best, bestPos, polygon);
|
||||
}
|
||||
}
|
||||
|
||||
return ClosestPolygonPoint(best, bestPos, polygon);
|
||||
}
|
||||
|
||||
ClosestPolygonPoint findClosest(Point from, Polygons& polygons)
|
||||
{
|
||||
|
||||
Polygon emptyPoly;
|
||||
ClosestPolygonPoint none(from, -1, emptyPoly);
|
||||
|
||||
if (polygons.size() == 0) return none;
|
||||
PolygonRef aPolygon = polygons[0];
|
||||
if (aPolygon.size() == 0) return none;
|
||||
Point aPoint = aPolygon[0];
|
||||
|
||||
ClosestPolygonPoint best(aPoint, 0, aPolygon);
|
||||
|
||||
int64_t closestDist = vSize2(from - best.location);
|
||||
|
||||
for (unsigned int ply = 0; ply < polygons.size(); ply++)
|
||||
{
|
||||
PolygonRef poly = polygons[ply];
|
||||
if (poly.size() == 0) continue;
|
||||
ClosestPolygonPoint closestHere = findClosest(from, poly);
|
||||
int64_t dist = vSize2(from - closestHere.location);
|
||||
if (dist < closestDist)
|
||||
{
|
||||
best = closestHere;
|
||||
closestDist = dist;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return best;
|
||||
}
|
||||
|
||||
ClosestPolygonPoint findClosest(Point from, PolygonRef polygon)
|
||||
{
|
||||
if (polygon.size() == 0)
|
||||
{
|
||||
return ClosestPolygonPoint(polygon);
|
||||
}
|
||||
Point aPoint = polygon[0];
|
||||
Point best = aPoint;
|
||||
|
||||
int64_t closestDist = vSize2(from - best);
|
||||
int bestPos = 0;
|
||||
//
|
||||
for (unsigned int p = 0; p<polygon.size(); p++)
|
||||
{
|
||||
Point& p1 = polygon[p];
|
||||
|
||||
unsigned int p2_idx = p+1;
|
||||
if (p2_idx >= polygon.size()) p2_idx = 0;
|
||||
Point& p2 = polygon[p2_idx];
|
||||
|
||||
Point closestHere = getClosestOnLine(from, p1 ,p2);
|
||||
int64_t dist = vSize2(from - closestHere);
|
||||
if (dist < closestDist)
|
||||
{
|
||||
best = closestHere;
|
||||
closestDist = dist;
|
||||
bestPos = p;
|
||||
}
|
||||
}
|
||||
|
||||
return ClosestPolygonPoint(best, bestPos, polygon);
|
||||
}
|
||||
|
||||
|
||||
Point getClosestOnLine(Point from, Point p0, Point p1)
|
||||
{
|
||||
Point direction = p1 - p0;
|
||||
Point toFrom = from-p0;
|
||||
int64_t projected_x = dot(toFrom, direction) ;
|
||||
|
||||
int64_t x_p0 = 0;
|
||||
int64_t x_p1 = vSize2(direction);
|
||||
|
||||
if (projected_x <= x_p0)
|
||||
{
|
||||
return p0;
|
||||
}
|
||||
if (projected_x >= x_p1)
|
||||
{
|
||||
return p1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (vSize2(direction) == 0)
|
||||
{
|
||||
std::cout << "warning! too small segment" << std::endl;
|
||||
return p0;
|
||||
}
|
||||
Point ret = p0 + projected_x / vSize(direction) * direction / vSize(direction);
|
||||
return ret ;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool getNextPointWithDistance(Point from, int64_t dist, const PolygonRef poly, int start_idx, int poly_start_idx, GivenDistPoint& result)
|
||||
{
|
||||
|
||||
Point prev_poly_point = poly[(start_idx + poly_start_idx) % poly.size()];
|
||||
|
||||
for (unsigned int prev_idx = start_idx; prev_idx < poly.size(); prev_idx++)
|
||||
{
|
||||
int next_idx = (prev_idx + 1 + poly_start_idx) % poly.size(); // last checked segment is between last point in poly and poly[0]...
|
||||
Point& next_poly_point = poly[next_idx];
|
||||
if ( !shorterThen(next_poly_point - from, dist) )
|
||||
{
|
||||
/*
|
||||
* x r
|
||||
* p.---------+---+------------.n
|
||||
* L| /
|
||||
* | / dist
|
||||
* |/
|
||||
* f.
|
||||
*
|
||||
* f=from
|
||||
* p=prev_poly_point
|
||||
* n=next_poly_point
|
||||
* x= f projected on pn
|
||||
* r=result point at distance [dist] from f
|
||||
*/
|
||||
|
||||
Point pn = next_poly_point - prev_poly_point;
|
||||
|
||||
if (shorterThen(pn, 100)) // when precision is limited
|
||||
{
|
||||
Point middle = (next_poly_point + prev_poly_point) / 2;
|
||||
int64_t dist_to_middle = vSize(from - middle);
|
||||
if (dist_to_middle - dist < 100 && dist_to_middle - dist > -100)
|
||||
{
|
||||
result.location = middle;
|
||||
result.pos = prev_idx;
|
||||
return true;
|
||||
} else
|
||||
{
|
||||
prev_poly_point = next_poly_point;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
Point pf = from - prev_poly_point;
|
||||
Point px = dot(pf, pn) / vSize(pn) * pn / vSize(pn);
|
||||
Point xf = pf - px;
|
||||
|
||||
if (!shorterThen(xf, dist)) // line lies wholly further than pn
|
||||
{
|
||||
prev_poly_point = next_poly_point;
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
int64_t xr_dist = std::sqrt(dist*dist - vSize2(xf)); // inverse Pythagoras
|
||||
|
||||
if (vSize(pn - px) - xr_dist < 1) // r lies beyond n
|
||||
{
|
||||
prev_poly_point = next_poly_point;
|
||||
continue;
|
||||
}
|
||||
|
||||
Point xr = xr_dist * pn / vSize(pn);
|
||||
Point pr = px + xr;
|
||||
|
||||
result.location = prev_poly_point + pr;
|
||||
result.pos = prev_idx;
|
||||
return true;
|
||||
}
|
||||
prev_poly_point = next_poly_point;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}//namespace cura
|
||||
@@ -0,0 +1,107 @@
|
||||
/** Copyright (C) 2015 Tim Kuipers - Released under terms of the AGPLv3 License */
|
||||
#ifndef POLYGON_UTILS_H
|
||||
#define POLYGON_UTILS_H
|
||||
|
||||
#include "polygon.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
//! performs an offset compared to an adjacent inset/outset and also computes the area created by gaps between the two consecutive insets/outsets
|
||||
void offsetExtrusionWidth(Polygons& poly, bool inward, int extrusionWidth, Polygons& result, Polygons* in_between, bool avoidOverlappingPerimeters);
|
||||
|
||||
//! performs an offset and makes sure the lines don't overlap (ignores any area between the original poly and the resulting poly)
|
||||
void offsetSafe(Polygons& poly, int distance, int extrusionWidth, Polygons& result, bool avoidOverlappingPerimeters);
|
||||
|
||||
//! performs offsets to make sure the lines don't overlap (ignores any area between the original poly and the resulting poly)
|
||||
void removeOverlapping(Polygons& poly, int extrusionWidth, Polygons& result);
|
||||
|
||||
/*!
|
||||
* Result of finding the closest point to a given within a set of polygons, with extra information on where the point is.
|
||||
*/
|
||||
struct ClosestPolygonPoint
|
||||
{
|
||||
Point location; //!< Result location
|
||||
PolygonRef poly; //!< Polygon in which the result was found
|
||||
int pos; //!< Index to the first point in the polygon of the line segment on which the result was found
|
||||
ClosestPolygonPoint(Point p, int pos, PolygonRef poly) : location(p), poly(poly), pos(pos) {};
|
||||
ClosestPolygonPoint(int pos, PolygonRef poly) : poly(poly), pos(pos) {};
|
||||
ClosestPolygonPoint(PolygonRef poly) : poly(poly), pos(-1) {};
|
||||
};
|
||||
|
||||
/*!
|
||||
* A point within a polygon and the index of which segment in the polygon the point lies on.
|
||||
*/
|
||||
struct GivenDistPoint
|
||||
{
|
||||
Point location; //!< Result location
|
||||
int pos; //!< Index to the first point in the polygon of the line segment on which the result was found
|
||||
};
|
||||
|
||||
/*!
|
||||
* Find the two points in two polygons with the smallest distance.
|
||||
*
|
||||
* \warning The ClosestPolygonPoint::poly fields output parameters should be initialized with the polygons for which to find the smallest connection.
|
||||
*
|
||||
* \param poly1_result Output parameter: the point at the one end of the smallest connection between its poly and \p poly2_result.poly.
|
||||
* \param poly2_result Output parameter: the point at the other end of the smallest connection between its poly and \p poly1_result.poly.
|
||||
* \param sample_size The number of points on each polygon to start the hill climbing search from.
|
||||
*/
|
||||
void findSmallestConnection(ClosestPolygonPoint& poly1_result, ClosestPolygonPoint& poly2_result, int sample_size);
|
||||
|
||||
/*!
|
||||
*
|
||||
* \warning Assumes \p poly1_result and \p poly2_result have their pos and poly fields initialized!
|
||||
*/
|
||||
void walkToNearestSmallestConnection(ClosestPolygonPoint& poly1_result, ClosestPolygonPoint& poly2_result);
|
||||
|
||||
/*!
|
||||
* Find the nearest closest point on a polygon from a given index.
|
||||
*
|
||||
* \param from The point from which to get the smallest distance.
|
||||
* \param polygon The polygon on which to find the point with the smallest distance.
|
||||
* \param start_idx The index of the point in the polygon from which to start looking.
|
||||
* \return The nearest point from \p start_idx going along the \p polygon (in both directions) with a locally minimal distance to \p from.
|
||||
*/
|
||||
ClosestPolygonPoint findNearestClosest(Point from, PolygonRef polygon, int start_idx);
|
||||
|
||||
/*!
|
||||
* Find the nearest closest point on a polygon from a given index walking in one direction along the polygon.
|
||||
*
|
||||
* \param from The point from which to get the smallest distance.
|
||||
* \param polygon The polygon on which to find the point with the smallest distance.
|
||||
* \param start_idx The index of the point in the polygon from which to start looking.
|
||||
* \param direction The direction to walk: 1 for walking along the \p polygon, -1 for walking in opposite direction
|
||||
* \return The nearest point from \p start_idx going along the \p polygon with a locally minimal distance to \p from.
|
||||
*/
|
||||
ClosestPolygonPoint findNearestClosest(Point from, PolygonRef polygon, int start_idx, int direction);
|
||||
|
||||
/*!
|
||||
* Find the point closest to \p from in all polygons in \p polygons.
|
||||
*/
|
||||
ClosestPolygonPoint findClosest(Point from, Polygons& polygons);
|
||||
|
||||
/*!
|
||||
* Find the point closest to \p from in the polygon \p polygon.
|
||||
*/
|
||||
ClosestPolygonPoint findClosest(Point from, PolygonRef polygon);
|
||||
|
||||
/*!
|
||||
* Find the point closest to \p from on the line from \p p0 to \p p1
|
||||
*/
|
||||
Point getClosestOnLine(Point from, Point p0, Point p1);
|
||||
|
||||
/*!
|
||||
* Find the next point (going along the direction of the polygon) with a distance \p dist from the point \p from within the \p poly.
|
||||
* Returns whether another point could be found within the \p poly which can be found before encountering the point at index \p start_idx.
|
||||
* The point \p from and the polygon \p poly are assumed to lie on the same plane.
|
||||
*
|
||||
* \param start_idx the index of the prev poly point on the poly.
|
||||
* \param poly_start_idx The index of the point in the polygon which is to be handled as the start of the polygon. No point further than this point will be the result.
|
||||
*/
|
||||
bool getNextPointWithDistance(Point from, int64_t dist, const PolygonRef poly, int start_idx, int poly_start_idx, GivenDistPoint& result);
|
||||
|
||||
|
||||
}//namespace cura
|
||||
|
||||
#endif//POLYGON_OPTIMIZER_H
|
||||
@@ -1 +1,60 @@
|
||||
#ifndef POLYGON_DEBUG_H
|
||||
#define POLYGON_DEBUG_H
|
||||
|
||||
#define DEBUG_SCALE 50.0
|
||||
|
||||
#include "polygon.h"
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
|
||||
namespace cura {
|
||||
|
||||
class PolygonDebug
|
||||
{
|
||||
private:
|
||||
FILE* f;
|
||||
public:
|
||||
|
||||
PolygonDebug(const char* filename)
|
||||
{
|
||||
f = fopen(filename, "w");
|
||||
fprintf(f, "<!DOCTYPE html><html><body>\n");
|
||||
|
||||
fprintf(f, "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" style='width:%ipx;height:%ipx'>\n", 1024 * 16, 1024 * 16);
|
||||
fprintf(f, "<marker id='MidMarker' viewBox='0 0 10 10' refX='5' refY='5' markerUnits='strokeWidth' markerWidth='10' markerHeight='10' stroke='lightblue' stroke-width='2' fill='none' orient='auto'>");
|
||||
fprintf(f, "<path d='M 0 0 L 10 5 M 0 10 L 10 5'/>");
|
||||
fprintf(f, "</marker>");
|
||||
}
|
||||
|
||||
~PolygonDebug()
|
||||
{
|
||||
fprintf(f, "</svg>\n");
|
||||
fprintf(f, "</body></html>");
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
void write(Polygons& polygons)
|
||||
{
|
||||
fprintf(f, "<g fill-rule='evenodd' style=\"fill: gray; stroke:black;stroke-width:1\">\n");
|
||||
fprintf(f, "<path marker-mid='url(#MidMarker)' d=\"");
|
||||
for(unsigned int j=0; j<polygons.size(); j++)
|
||||
{
|
||||
PolygonRef p = polygons[j];
|
||||
for(unsigned int n=0; n<p.size(); n++)
|
||||
{
|
||||
if (n == 0)
|
||||
fprintf(f, "M");
|
||||
else
|
||||
fprintf(f, "L");
|
||||
fprintf(f, "%f,%f ", float(p[n].X)/DEBUG_SCALE, float(p[n].Y)/DEBUG_SCALE);
|
||||
}
|
||||
fprintf(f, "Z\n");
|
||||
}
|
||||
fprintf(f, "\"/>");
|
||||
fprintf(f, "</g>\n");
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif//POLYGON_DEBUG_H
|
||||
|
||||
+26
-8
@@ -43,7 +43,7 @@ void ClientSocket::connectTo(std::string host, int port)
|
||||
serv_addr.sin_family = AF_INET;
|
||||
serv_addr.sin_port = htons(port);
|
||||
serv_addr.sin_addr.s_addr = inet_addr(host.c_str());
|
||||
// TODO: Check this: C style cast replaced by reinterpret_cast!!
|
||||
|
||||
if (connect(sockfd, reinterpret_cast<struct sockaddr*>(&serv_addr), sizeof(serv_addr)) < 0)
|
||||
{
|
||||
printf("Connect to %s:%d failed\n", host.c_str(), port);
|
||||
@@ -57,11 +57,17 @@ ClientSocket::~ClientSocket()
|
||||
close();
|
||||
}
|
||||
|
||||
void ClientSocket::sendNr(int nr)
|
||||
void ClientSocket::sendInt32(int32_t nr)
|
||||
{
|
||||
sendAll(&nr, sizeof(int));
|
||||
sendAll(&nr, sizeof(int32_t));
|
||||
}
|
||||
|
||||
void ClientSocket::sendFloat32(float f)
|
||||
{
|
||||
sendAll(&f, sizeof(float));
|
||||
}
|
||||
|
||||
|
||||
void ClientSocket::sendAll(const void* data, int length)
|
||||
{
|
||||
if (sockfd == -1)
|
||||
@@ -80,10 +86,17 @@ void ClientSocket::sendAll(const void* data, int length)
|
||||
}
|
||||
}
|
||||
|
||||
int ClientSocket::recvNr()
|
||||
int32_t ClientSocket::recvInt32()
|
||||
{
|
||||
int ret = 0;
|
||||
recvAll(&ret, 4);
|
||||
int32_t ret = -1;
|
||||
recvAll(&ret, sizeof(int32_t));
|
||||
return ret;
|
||||
}
|
||||
|
||||
float ClientSocket::recvFloat32()
|
||||
{
|
||||
float ret = 0;
|
||||
recvAll(&ret, sizeof(float));
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -95,9 +108,14 @@ void ClientSocket::recvAll(void* data, int length)
|
||||
while(length > 0)
|
||||
{
|
||||
int n = recv(sockfd, ptr, length, 0);
|
||||
if (n <= 0)
|
||||
if (n == 0)
|
||||
{
|
||||
cura::log("ClientSocket::recvAll error...");
|
||||
close();
|
||||
return;
|
||||
}
|
||||
if (n < 0)
|
||||
{
|
||||
cura::logError("ClientSocket::recvAll error...");
|
||||
close();
|
||||
return;
|
||||
}
|
||||
|
||||
+25
-22
@@ -1,23 +1,26 @@
|
||||
#ifndef SOCKET_H
|
||||
#define SOCKET_H
|
||||
|
||||
#include <string>
|
||||
|
||||
class ClientSocket
|
||||
{
|
||||
int sockfd;
|
||||
public:
|
||||
ClientSocket();
|
||||
~ClientSocket();
|
||||
|
||||
void connectTo(std::string host, int port);
|
||||
|
||||
void sendNr(int nr);
|
||||
#ifndef SOCKET_H
|
||||
#define SOCKET_H
|
||||
|
||||
#include <string>
|
||||
|
||||
class ClientSocket
|
||||
{
|
||||
int sockfd;
|
||||
public:
|
||||
ClientSocket();
|
||||
~ClientSocket();
|
||||
|
||||
void connectTo(std::string host, int port);
|
||||
|
||||
void sendInt32(int32_t nr);
|
||||
void sendFloat32(float f);
|
||||
void sendAll(const void* data, int length);
|
||||
int recvNr();
|
||||
void recvAll(void* data, int length);
|
||||
|
||||
void close();
|
||||
};
|
||||
|
||||
#endif//SOCKET_H
|
||||
|
||||
int32_t recvInt32();
|
||||
float recvFloat32();
|
||||
void recvAll(void* data, int length);
|
||||
|
||||
void close();
|
||||
};
|
||||
|
||||
#endif//SOCKET_H
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#ifndef STRING_H
|
||||
#define STRING_H
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
//c++11 no longer supplies a strcasecmp, so define our own version.
|
||||
static inline int stringcasecompare(const char* a, const char* b)
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
|
||||
#ifndef WEAVE_DATA_STORAGE_H
|
||||
#define WEAVE_DATA_STORAGE_H
|
||||
|
||||
#include "utils/intpoint.h"
|
||||
#include "utils/polygon.h"
|
||||
#include "mesh.h"
|
||||
#include "gcodePlanner.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
namespace cura {
|
||||
|
||||
|
||||
ENUM( WeaveSegmentType, UP, DOWN, FLAT, MOVE, DOWN_AND_FLAT); // DOWN_AND_FLAT is for parts of the roof which can either be viewed as flat or as down, since their [to] location is an up move with zero length
|
||||
|
||||
|
||||
struct WeaveConnectionSegment
|
||||
{
|
||||
Point3 to;
|
||||
WeaveSegmentType segmentType;
|
||||
WeaveConnectionSegment(Point3 to, WeaveSegmentType dir) : to(to), segmentType(dir) {};
|
||||
};
|
||||
|
||||
struct PolyLine3
|
||||
{
|
||||
Point3 from;
|
||||
std::vector<WeaveConnectionSegment> segments;
|
||||
};
|
||||
|
||||
struct WeaveConnectionPart
|
||||
{
|
||||
PolyLine3 connection;
|
||||
int supported_index;//!< index of corresponding supported polygon in WeaveConnection.supported (! last point in polygon is first point to start printing it!)
|
||||
WeaveConnectionPart(int top_idx) : supported_index(top_idx) {};
|
||||
};
|
||||
|
||||
struct WeaveConnection
|
||||
{
|
||||
int z0;//!< height of the supporting polygons (of the prev layer, roof inset, etc.)
|
||||
int z1;//!< height of the \p supported polygons
|
||||
std::vector<WeaveConnectionPart> connections; //!< for each polygon in \p supported the connection.
|
||||
Polygons supported; //!< polygons to be supported by connections (from other polygons)
|
||||
};
|
||||
|
||||
// Horizontal Fills:
|
||||
|
||||
typedef std::vector<WeaveConnectionSegment> WeaveInsetPart; //!< Polygon with extra information on each point
|
||||
struct WeaveRoofPart : WeaveConnection
|
||||
{
|
||||
// [supported] is an insets of the roof polygons (or of previous insets of it)
|
||||
// [connections] are the connections between two consecutive roof polygon insets
|
||||
std::vector<WeaveInsetPart> supported_withMoves; //!< optimized inset polygons, with some parts of the polygons replaced by moves
|
||||
};
|
||||
|
||||
struct WeaveRoof
|
||||
{
|
||||
std::vector<WeaveRoofPart> roof_insets; //!< connections between consecutive insets of the roof polygons
|
||||
Polygons roof_outlines; //!< the area within which the horitonal connections are generated
|
||||
};
|
||||
|
||||
// Layers
|
||||
|
||||
struct WeaveLayer : WeaveConnection
|
||||
{
|
||||
// [supported] are the outline polygons on the next layer which are (to be) connected,
|
||||
// as well as the polygons supported by roofs (holes and boundaries of roofs)
|
||||
// [connections] are the vertical connections
|
||||
WeaveRoof roofs; //!< parts which are filled horizontally (both roofs and floors...)
|
||||
};
|
||||
struct WireFrame
|
||||
{
|
||||
WeaveRoof bottom_infill;
|
||||
Polygons bottom_outline;
|
||||
int z_bottom;
|
||||
std::vector<WeaveLayer> layers;
|
||||
};
|
||||
|
||||
|
||||
}//namespace cura
|
||||
|
||||
#endif//WEAVE_DATA_STORAGE_H
|
||||
+21
-7
@@ -2,13 +2,27 @@
|
||||
|
||||
import sys
|
||||
import subprocess
|
||||
import os
|
||||
import time
|
||||
import stat
|
||||
|
||||
def main(engine):
|
||||
p = subprocess.Popen([engine, '-c', 'supportAngle=60', '-c', 'supportEverywhere=1', '_tests/testModel.stl'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
p.communicate()
|
||||
if p.wait() != 0:
|
||||
print "Engine failed to report success on test object slice..."
|
||||
sys.exit(1)
|
||||
def main(engine, model_path):
|
||||
filenames = sorted(os.listdir(model_path), key=lambda filename: os.stat(os.path.join(model_path, filename)).st_size)
|
||||
filenames = list(filter(lambda filename: filename.lower().endswith('.stl'), filenames))
|
||||
for filename in filenames:
|
||||
print("Slicing: %s (%d/%d)" % (filename, filenames.index(filename), len(filenames)))
|
||||
t = time.time()
|
||||
p = subprocess.Popen([engine, '-vv', os.path.join(model_path, filename)], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
stdout, stderr = p.communicate()
|
||||
if p.wait() != 0:
|
||||
print ("Engine failed to report success on test object: %s" % (filename))
|
||||
print(stderr.decode('utf-8', 'replace').split('\n')[-5:])
|
||||
sys.exit(1)
|
||||
else:
|
||||
print("Slicing took: %f" % (time.time() - t))
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(sys.argv[1])
|
||||
model_path = 'tests'
|
||||
if len(sys.argv) > 2:
|
||||
model_path = sys.argv[2]
|
||||
main(sys.argv[1], model_path)
|
||||
|
||||
Referência em uma Nova Issue
Bloquear um usuário