Explorar o código

Add surface 3D, wireframe Surface 3D, contour, and wireframe contour chart support

xuri %!s(int64=6) %!d(string=hai) anos
pai
achega
5cf1c05ed4
Modificáronse 3 ficheiros con 183 adicións e 40 borrados
  1. 133 6
      chart.go
  2. 7 3
      chart_test.go
  3. 43 31
      xmlChart.go

+ 133 - 6
chart.go

@@ -65,6 +65,10 @@ const (
 	Pie3D                       = "pie3D"
 	Radar                       = "radar"
 	Scatter                     = "scatter"
+	Surface3D                   = "surface3D"
+	WireframeSurface3D          = "wireframeSurface3D"
+	Contour                     = "contour"
+	WireframeContour            = "wireframeContour"
 )
 
 // This section defines the default value of chart properties.
@@ -116,6 +120,10 @@ var (
 		Pie3D:                       30,
 		Radar:                       0,
 		Scatter:                     0,
+		Surface3D:                   15,
+		WireframeSurface3D:          15,
+		Contour:                     90,
+		WireframeContour:            90,
 	}
 	chartView3DRotY = map[string]int{
 		Area:                        0,
@@ -164,6 +172,10 @@ var (
 		Pie3D:                       0,
 		Radar:                       0,
 		Scatter:                     0,
+		Surface3D:                   20,
+		WireframeSurface3D:          20,
+		Contour:                     0,
+		WireframeContour:            0,
 	}
 	chartView3DDepthPercent = map[string]int{
 		Area:                        100,
@@ -212,6 +224,14 @@ var (
 		Pie3D:                       100,
 		Radar:                       100,
 		Scatter:                     100,
+		Surface3D:                   100,
+		WireframeSurface3D:          100,
+		Contour:                     100,
+		WireframeContour:            100,
+	}
+	chartView3DPerspective = map[string]int{
+		Contour:          0,
+		WireframeContour: 0,
 	}
 	chartView3DRAngAx = map[string]int{
 		Area:                        0,
@@ -260,6 +280,9 @@ var (
 		Pie3D:                       0,
 		Radar:                       0,
 		Scatter:                     0,
+		Surface3D:                   0,
+		WireframeSurface3D:          0,
+		Contour:                     0,
 	}
 	chartLegendPosition = map[string]string{
 		"bottom":    "b",
@@ -315,6 +338,10 @@ var (
 		Pie3D:                       "General",
 		Radar:                       "General",
 		Scatter:                     "General",
+		Surface3D:                   "General",
+		WireframeSurface3D:          "General",
+		Contour:                     "General",
+		WireframeContour:            "General",
 	}
 	chartValAxCrossBetween = map[string]string{
 		Area:                        "midCat",
@@ -363,6 +390,10 @@ var (
 		Pie3D:                       "between",
 		Radar:                       "between",
 		Scatter:                     "between",
+		Surface3D:                   "midCat",
+		WireframeSurface3D:          "midCat",
+		Contour:                     "midCat",
+		WireframeContour:            "midCat",
 	}
 	plotAreaChartGrouping = map[string]string{
 		Area:                        "standard",
@@ -456,6 +487,10 @@ var (
 		true:  "r",
 		false: "l",
 	}
+	valTickLblPos = map[string]string{
+		Contour:          "none",
+		WireframeContour: "none",
+	}
 )
 
 // parseFormatChartSet provides a function to parse the format settings of the
@@ -573,6 +608,10 @@ func parseFormatChartSet(formatSet string) (*formatChart, error) {
 //     pie3D                       | 3D pie chart
 //     radar                       | radar chart
 //     scatter                     | scatter chart
+//     surface3D                   | 3D surface chart
+//     wireframeSurface3D          | 3D wireframe surface chart
+//     contour                     | contour chart
+//     wireframeContour            | wireframe contour
 //
 // In Excel a chart series is a collection of information that defines which data is plotted such as values, axis labels and formatting.
 //
@@ -791,6 +830,7 @@ func (f *File) addChart(formatSet *formatChart) {
 				RotX:         &attrValInt{Val: chartView3DRotX[formatSet.Type]},
 				RotY:         &attrValInt{Val: chartView3DRotY[formatSet.Type]},
 				DepthPercent: &attrValInt{Val: chartView3DDepthPercent[formatSet.Type]},
+				Perspective:  &attrValInt{Val: chartView3DPerspective[formatSet.Type]},
 				RAngAx:       &attrValInt{Val: chartView3DRAngAx[formatSet.Type]},
 			},
 			Floor: &cThicknessSpPr{
@@ -891,6 +931,10 @@ func (f *File) addChart(formatSet *formatChart) {
 		Pie:                         f.drawPieChart,
 		Radar:                       f.drawRadarChart,
 		Scatter:                     f.drawScatterChart,
+		Surface3D:                   f.drawSurface3DChart,
+		WireframeSurface3D:          f.drawSurface3DChart,
+		Contour:                     f.drawSurfaceChart,
+		WireframeContour:            f.drawSurfaceChart,
 	}
 	xlsxChartSpace.Chart.PlotArea = plotAreaFunc[formatSet.Type](formatSet)
 
@@ -1248,6 +1292,52 @@ func (f *File) drawScatterChart(formatSet *formatChart) *cPlotArea {
 	}
 }
 
+// drawSurface3DChart provides a function to draw the c:surface3DChart element by
+// given format sets.
+func (f *File) drawSurface3DChart(formatSet *formatChart) *cPlotArea {
+	plotArea := &cPlotArea{
+		Surface3DChart: &cCharts{
+			Ser: f.drawChartSeries(formatSet),
+			AxID: []*attrValInt{
+				{Val: 754001152},
+				{Val: 753999904},
+				{Val: 832256642},
+			},
+		},
+		CatAx: f.drawPlotAreaCatAx(formatSet),
+		ValAx: f.drawPlotAreaValAx(formatSet),
+		SerAx: f.drawPlotAreaSerAx(formatSet),
+	}
+	if formatSet.Type == WireframeSurface3D {
+		plotArea.Surface3DChart.Wireframe = &attrValBool{Val: true}
+	}
+	return plotArea
+}
+
+// drawSurfaceChart provides a function to draw the c:surfaceChart element by
+// given format sets.
+func (f *File) drawSurfaceChart(formatSet *formatChart) *cPlotArea {
+	plotArea := &cPlotArea{
+		SurfaceChart: &cCharts{
+			Ser: f.drawChartSeries(formatSet),
+			AxID: []*attrValInt{
+				{Val: 754001152},
+				{Val: 753999904},
+				{Val: 832256642},
+			},
+		},
+		CatAx: f.drawPlotAreaCatAx(formatSet),
+		ValAx: f.drawPlotAreaValAx(formatSet),
+		SerAx: f.drawPlotAreaSerAx(formatSet),
+	}
+	if formatSet.Type == WireframeContour {
+		plotArea.SurfaceChart.Wireframe = &attrValBool{Val: true}
+	}
+	return plotArea
+}
+
+// drawChartShape provides a function to draw the c:shape element by given
+// format sets.
 func (f *File) drawChartShape(formatSet *formatChart) *attrValString {
 	shapes := map[string]string{
 		Bar3DConeClustered:          "cone",
@@ -1273,9 +1363,7 @@ func (f *File) drawChartShape(formatSet *formatChart) *attrValString {
 		Col3DCylinderPercentStacked: "cylinder",
 	}
 	if shape, ok := shapes[formatSet.Type]; ok {
-		return &attrValString{
-			Val: shape,
-		}
+		return &attrValString{Val: shape}
 	}
 	return nil
 }
@@ -1459,7 +1547,7 @@ func (f *File) drawChartDLbls(formatSet *formatChart) *cDLbls {
 // given format sets.
 func (f *File) drawChartSeriesDLbls(formatSet *formatChart) *cDLbls {
 	dLbls := f.drawChartDLbls(formatSet)
-	chartSeriesDLbls := map[string]*cDLbls{Scatter: nil}
+	chartSeriesDLbls := map[string]*cDLbls{Scatter: nil, Surface3D: nil, WireframeSurface3D: nil, Contour: nil, WireframeContour: nil}
 	if _, ok := chartSeriesDLbls[formatSet.Type]; ok {
 		return nil
 	}
@@ -1476,7 +1564,7 @@ func (f *File) drawPlotAreaCatAx(formatSet *formatChart) []*cAxs {
 	if formatSet.XAxis.Maximum == 0 {
 		max = nil
 	}
-	return []*cAxs{
+	axs := []*cAxs{
 		{
 			AxID: &attrValInt{Val: 754001152},
 			Scaling: &cScaling{
@@ -1503,6 +1591,10 @@ func (f *File) drawPlotAreaCatAx(formatSet *formatChart) []*cAxs {
 			NoMultiLvlLbl: &attrValBool{Val: false},
 		},
 	}
+	if formatSet.XAxis.MajorGridlines {
+		axs[0].MajorGridlines = &cChartLines{SpPr: f.drawPlotAreaSpPr()}
+	}
+	return axs
 }
 
 // drawPlotAreaValAx provides a function to draw the c:valAx element.
@@ -1515,7 +1607,7 @@ func (f *File) drawPlotAreaValAx(formatSet *formatChart) []*cAxs {
 	if formatSet.YAxis.Maximum == 0 {
 		max = nil
 	}
-	return []*cAxs{
+	axs := []*cAxs{
 		{
 			AxID: &attrValInt{Val: 753999904},
 			Scaling: &cScaling{
@@ -1539,6 +1631,41 @@ func (f *File) drawPlotAreaValAx(formatSet *formatChart) []*cAxs {
 			CrossBetween:  &attrValString{Val: chartValAxCrossBetween[formatSet.Type]},
 		},
 	}
+	if formatSet.YAxis.MajorGridlines {
+		axs[0].MajorGridlines = &cChartLines{SpPr: f.drawPlotAreaSpPr()}
+	}
+	if pos, ok := valTickLblPos[formatSet.Type]; ok {
+		axs[0].TickLblPos.Val = pos
+	}
+	return axs
+}
+
+// drawPlotAreaSerAx provides a function to draw the c:serAx element.
+func (f *File) drawPlotAreaSerAx(formatSet *formatChart) []*cAxs {
+	min := &attrValFloat{Val: formatSet.YAxis.Minimum}
+	max := &attrValFloat{Val: formatSet.YAxis.Maximum}
+	if formatSet.YAxis.Minimum == 0 {
+		min = nil
+	}
+	if formatSet.YAxis.Maximum == 0 {
+		max = nil
+	}
+	return []*cAxs{
+		{
+			AxID: &attrValInt{Val: 832256642},
+			Scaling: &cScaling{
+				Orientation: &attrValString{Val: orientation[formatSet.YAxis.ReverseOrder]},
+				Max:         max,
+				Min:         min,
+			},
+			Delete:     &attrValBool{Val: false},
+			AxPos:      &attrValString{Val: catAxPos[formatSet.XAxis.ReverseOrder]},
+			TickLblPos: &attrValString{Val: "nextTo"},
+			SpPr:       f.drawPlotAreaSpPr(),
+			TxPr:       f.drawPlotAreaTxPr(),
+			CrossAx:    &attrValInt{Val: 753999904},
+		},
+	}
 }
 
 // drawPlotAreaSpPr provides a function to draw the c:spPr element.

+ 7 - 3
chart_test.go

@@ -160,17 +160,21 @@ func TestAddChart(t *testing.T) {
 	assert.NoError(t, f.AddChart("Sheet2", "AN16", `{"type":"area3D","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"3D Area Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`))
 	assert.NoError(t, f.AddChart("Sheet2", "AF32", `{"type":"area3DStacked","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"3D Stacked Area Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`))
 	assert.NoError(t, f.AddChart("Sheet2", "AN32", `{"type":"area3DPercentStacked","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"3D 100% Stacked Area Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`))
-
+	// cylinder series chart
 	assert.NoError(t, f.AddChart("Sheet2", "AF48", `{"type":"bar3DCylinderStacked","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"3D Bar Cylinder Stacked Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`))
 	assert.NoError(t, f.AddChart("Sheet2", "AF64", `{"type":"bar3DCylinderClustered","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"3D Bar Cylinder Clustered Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`))
 	assert.NoError(t, f.AddChart("Sheet2", "AF80", `{"type":"bar3DCylinderPercentStacked","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"3D Bar Cylinder Percent Stacked Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`))
-
+	// cone series chart
 	assert.NoError(t, f.AddChart("Sheet2", "AN48", `{"type":"bar3DConeStacked","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"3D Bar Cone Stacked Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`))
 	assert.NoError(t, f.AddChart("Sheet2", "AN64", `{"type":"bar3DConeClustered","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"3D Bar Cone Clustered Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`))
 	assert.NoError(t, f.AddChart("Sheet2", "AN80", `{"type":"bar3DConePercentStacked","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"3D Bar Cone Percent Stacked Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`))
 	assert.NoError(t, f.AddChart("Sheet2", "AV48", `{"type":"bar3DPyramidStacked","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"3D Bar Pyramid Stacked Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`))
 	assert.NoError(t, f.AddChart("Sheet2", "AV64", `{"type":"bar3DPyramidClustered","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"3D Bar Pyramid Clustered Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`))
 	assert.NoError(t, f.AddChart("Sheet2", "AV80", `{"type":"bar3DPyramidPercentStacked","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"3D Bar Pyramid Percent Stacked Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`))
-
+	// surface series chart
+	assert.NoError(t, f.AddChart("Sheet2", "AV1", `{"type":"surface3D","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"3D Surface Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero","y_axis":{"major_grid_lines":true}}`))
+	assert.NoError(t, f.AddChart("Sheet2", "AV16", `{"type":"wireframeSurface3D","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"3D Wireframe Surface Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero","y_axis":{"major_grid_lines":true}}`))
+	assert.NoError(t, f.AddChart("Sheet2", "AV30", `{"type":"contour","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"Contour Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`))
+	assert.NoError(t, f.AddChart("Sheet2", "BD1", `{"type":"wireframeContour","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"Wireframe Contour Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`))
 	assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAddChart.xlsx")))
 }

+ 43 - 31
xmlChart.go

@@ -294,26 +294,30 @@ type cView3D struct {
 	RotX         *attrValInt `xml:"rotX"`
 	RotY         *attrValInt `xml:"rotY"`
 	DepthPercent *attrValInt `xml:"depthPercent"`
+	Perspective  *attrValInt `xml:"perspective"`
 	RAngAx       *attrValInt `xml:"rAngAx"`
 }
 
 // cPlotArea directly maps the plotArea element. This element specifies the
 // plot area of the chart.
 type cPlotArea struct {
-	Layout        *string  `xml:"layout"`
-	AreaChart     *cCharts `xml:"areaChart"`
-	Area3DChart   *cCharts `xml:"area3DChart"`
-	BarChart      *cCharts `xml:"barChart"`
-	Bar3DChart    *cCharts `xml:"bar3DChart"`
-	DoughnutChart *cCharts `xml:"doughnutChart"`
-	LineChart     *cCharts `xml:"lineChart"`
-	PieChart      *cCharts `xml:"pieChart"`
-	Pie3DChart    *cCharts `xml:"pie3DChart"`
-	RadarChart    *cCharts `xml:"radarChart"`
-	ScatterChart  *cCharts `xml:"scatterChart"`
-	CatAx         []*cAxs  `xml:"catAx"`
-	ValAx         []*cAxs  `xml:"valAx"`
-	SpPr          *cSpPr   `xml:"spPr"`
+	Layout         *string  `xml:"layout"`
+	AreaChart      *cCharts `xml:"areaChart"`
+	Area3DChart    *cCharts `xml:"area3DChart"`
+	BarChart       *cCharts `xml:"barChart"`
+	Bar3DChart     *cCharts `xml:"bar3DChart"`
+	DoughnutChart  *cCharts `xml:"doughnutChart"`
+	LineChart      *cCharts `xml:"lineChart"`
+	PieChart       *cCharts `xml:"pieChart"`
+	Pie3DChart     *cCharts `xml:"pie3DChart"`
+	RadarChart     *cCharts `xml:"radarChart"`
+	ScatterChart   *cCharts `xml:"scatterChart"`
+	Surface3DChart *cCharts `xml:"surface3DChart"`
+	SurfaceChart   *cCharts `xml:"surfaceChart"`
+	CatAx          []*cAxs  `xml:"catAx"`
+	ValAx          []*cAxs  `xml:"valAx"`
+	SerAx          []*cAxs  `xml:"serAx"`
+	SpPr           *cSpPr   `xml:"spPr"`
 }
 
 // cCharts specifies the common element of the chart.
@@ -323,6 +327,7 @@ type cCharts struct {
 	RadarStyle   *attrValString `xml:"radarStyle"`
 	ScatterStyle *attrValString `xml:"scatterStyle"`
 	VaryColors   *attrValBool   `xml:"varyColors"`
+	Wireframe    *attrValBool   `xml:"wireframe"`
 	Ser          *[]cSer        `xml:"ser"`
 	Shape        *attrValString `xml:"shape"`
 	DLbls        *cDLbls        `xml:"dLbls"`
@@ -334,23 +339,29 @@ type cCharts struct {
 
 // cAxs directly maps the catAx and valAx element.
 type cAxs struct {
-	AxID          *attrValInt    `xml:"axId"`
-	Scaling       *cScaling      `xml:"scaling"`
-	Delete        *attrValBool   `xml:"delete"`
-	AxPos         *attrValString `xml:"axPos"`
-	NumFmt        *cNumFmt       `xml:"numFmt"`
-	MajorTickMark *attrValString `xml:"majorTickMark"`
-	MinorTickMark *attrValString `xml:"minorTickMark"`
-	TickLblPos    *attrValString `xml:"tickLblPos"`
-	SpPr          *cSpPr         `xml:"spPr"`
-	TxPr          *cTxPr         `xml:"txPr"`
-	CrossAx       *attrValInt    `xml:"crossAx"`
-	Crosses       *attrValString `xml:"crosses"`
-	CrossBetween  *attrValString `xml:"crossBetween"`
-	Auto          *attrValBool   `xml:"auto"`
-	LblAlgn       *attrValString `xml:"lblAlgn"`
-	LblOffset     *attrValInt    `xml:"lblOffset"`
-	NoMultiLvlLbl *attrValBool   `xml:"noMultiLvlLbl"`
+	AxID           *attrValInt    `xml:"axId"`
+	Scaling        *cScaling      `xml:"scaling"`
+	Delete         *attrValBool   `xml:"delete"`
+	AxPos          *attrValString `xml:"axPos"`
+	MajorGridlines *cChartLines   `xml:"majorGridlines"`
+	NumFmt         *cNumFmt       `xml:"numFmt"`
+	MajorTickMark  *attrValString `xml:"majorTickMark"`
+	MinorTickMark  *attrValString `xml:"minorTickMark"`
+	TickLblPos     *attrValString `xml:"tickLblPos"`
+	SpPr           *cSpPr         `xml:"spPr"`
+	TxPr           *cTxPr         `xml:"txPr"`
+	CrossAx        *attrValInt    `xml:"crossAx"`
+	Crosses        *attrValString `xml:"crosses"`
+	CrossBetween   *attrValString `xml:"crossBetween"`
+	Auto           *attrValBool   `xml:"auto"`
+	LblAlgn        *attrValString `xml:"lblAlgn"`
+	LblOffset      *attrValInt    `xml:"lblOffset"`
+	NoMultiLvlLbl  *attrValBool   `xml:"noMultiLvlLbl"`
+}
+
+// cChartLines directly maps the chart lines content model.
+type cChartLines struct {
+	SpPr *cSpPr `xml:"spPr"`
 }
 
 // cScaling directly maps the scaling element. This element contains
@@ -497,6 +508,7 @@ type cPageMargins struct {
 // formatChartAxis directly maps the format settings of the chart axis.
 type formatChartAxis struct {
 	Crossing            string  `json:"crossing"`
+	MajorGridlines      bool    `json:"major_grid_lines"`
 	MajorTickMark       string  `json:"major_tick_mark"`
 	MinorTickMark       string  `json:"minor_tick_mark"`
 	MinorUnitType       string  `json:"minor_unit_type"`