You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1131 lines
41 KiB

14 years ago
14 years ago
14 years ago
17 years ago
17 years ago
17 years ago
14 years ago
14 years ago
14 years ago
17 years ago
17 years ago
Clean up arc/circle polygonization. 1) For a while now we've been using a calculated seg count from a given maxError, and a correction factor to push the radius out so that all the error is outside the arc/circle. However, the second calculation (which pre-dates the first) is pretty much just the inverse of the first (and yields nothing more than maxError back). This is particularly sub-optimal given the cost of trig functions. 2) There are a lot of old optimizations to reduce segcounts in certain situations, someting that our error-based calculation compensates for anyway. (Smaller radii need fewer segments to meet the maxError condition.) But perhaps more importantly we now surface maxError in the UI and we don't really want to call it "Max deviation except when it's not". 3) We were also clamping the segCount twice: once in the calculation routine and once in most of it's callers. Furthermore, the caller clamping was inconsistent (both in being done and in the clamping value). We now clamp only in the calculation routine. 4) There's no reason to use the correction factors in the 3Dviewer; it's just a visualization and whether the polygonization error is inside or outside the shape isn't really material. 5) The arc-correction-disabling stuff (used for solder mask layer) was somewhat fragile in that it depended on the caller to turn it back on afterwards. It's now only exposed as a RAII object which automatically cleans up when it goes out of scope. 6) There were also bugs in a couple of the polygonization routines where we'd accumulate round-off error in adding up the segments and end up with an overly long last segment (which of course would voilate the error max). This was the cause of the linked bug and also some issues with vias that we had fudged in the past with extra clearance. Fixes https://gitlab.com/kicad/code/kicad/issues/5567
5 years ago
17 years ago
14 years ago
14 years ago
17 years ago
Clean up arc/circle polygonization. 1) For a while now we've been using a calculated seg count from a given maxError, and a correction factor to push the radius out so that all the error is outside the arc/circle. However, the second calculation (which pre-dates the first) is pretty much just the inverse of the first (and yields nothing more than maxError back). This is particularly sub-optimal given the cost of trig functions. 2) There are a lot of old optimizations to reduce segcounts in certain situations, someting that our error-based calculation compensates for anyway. (Smaller radii need fewer segments to meet the maxError condition.) But perhaps more importantly we now surface maxError in the UI and we don't really want to call it "Max deviation except when it's not". 3) We were also clamping the segCount twice: once in the calculation routine and once in most of it's callers. Furthermore, the caller clamping was inconsistent (both in being done and in the clamping value). We now clamp only in the calculation routine. 4) There's no reason to use the correction factors in the 3Dviewer; it's just a visualization and whether the polygonization error is inside or outside the shape isn't really material. 5) The arc-correction-disabling stuff (used for solder mask layer) was somewhat fragile in that it depended on the caller to turn it back on afterwards. It's now only exposed as a RAII object which automatically cleans up when it goes out of scope. 6) There were also bugs in a couple of the polygonization routines where we'd accumulate round-off error in adding up the segments and end up with an overly long last segment (which of course would voilate the error max). This was the cause of the linked bug and also some issues with vias that we had fudged in the past with extra clearance. Fixes https://gitlab.com/kicad/code/kicad/issues/5567
5 years ago
Clean up arc/circle polygonization. 1) For a while now we've been using a calculated seg count from a given maxError, and a correction factor to push the radius out so that all the error is outside the arc/circle. However, the second calculation (which pre-dates the first) is pretty much just the inverse of the first (and yields nothing more than maxError back). This is particularly sub-optimal given the cost of trig functions. 2) There are a lot of old optimizations to reduce segcounts in certain situations, someting that our error-based calculation compensates for anyway. (Smaller radii need fewer segments to meet the maxError condition.) But perhaps more importantly we now surface maxError in the UI and we don't really want to call it "Max deviation except when it's not". 3) We were also clamping the segCount twice: once in the calculation routine and once in most of it's callers. Furthermore, the caller clamping was inconsistent (both in being done and in the clamping value). We now clamp only in the calculation routine. 4) There's no reason to use the correction factors in the 3Dviewer; it's just a visualization and whether the polygonization error is inside or outside the shape isn't really material. 5) The arc-correction-disabling stuff (used for solder mask layer) was somewhat fragile in that it depended on the caller to turn it back on afterwards. It's now only exposed as a RAII object which automatically cleans up when it goes out of scope. 6) There were also bugs in a couple of the polygonization routines where we'd accumulate round-off error in adding up the segments and end up with an overly long last segment (which of course would voilate the error max). This was the cause of the linked bug and also some issues with vias that we had fudged in the past with extra clearance. Fixes https://gitlab.com/kicad/code/kicad/issues/5567
5 years ago
Clean up arc/circle polygonization. 1) For a while now we've been using a calculated seg count from a given maxError, and a correction factor to push the radius out so that all the error is outside the arc/circle. However, the second calculation (which pre-dates the first) is pretty much just the inverse of the first (and yields nothing more than maxError back). This is particularly sub-optimal given the cost of trig functions. 2) There are a lot of old optimizations to reduce segcounts in certain situations, someting that our error-based calculation compensates for anyway. (Smaller radii need fewer segments to meet the maxError condition.) But perhaps more importantly we now surface maxError in the UI and we don't really want to call it "Max deviation except when it's not". 3) We were also clamping the segCount twice: once in the calculation routine and once in most of it's callers. Furthermore, the caller clamping was inconsistent (both in being done and in the clamping value). We now clamp only in the calculation routine. 4) There's no reason to use the correction factors in the 3Dviewer; it's just a visualization and whether the polygonization error is inside or outside the shape isn't really material. 5) The arc-correction-disabling stuff (used for solder mask layer) was somewhat fragile in that it depended on the caller to turn it back on afterwards. It's now only exposed as a RAII object which automatically cleans up when it goes out of scope. 6) There were also bugs in a couple of the polygonization routines where we'd accumulate round-off error in adding up the segments and end up with an overly long last segment (which of course would voilate the error max). This was the cause of the linked bug and also some issues with vias that we had fudged in the past with extra clearance. Fixes https://gitlab.com/kicad/code/kicad/issues/5567
5 years ago
Clean up arc/circle polygonization. 1) For a while now we've been using a calculated seg count from a given maxError, and a correction factor to push the radius out so that all the error is outside the arc/circle. However, the second calculation (which pre-dates the first) is pretty much just the inverse of the first (and yields nothing more than maxError back). This is particularly sub-optimal given the cost of trig functions. 2) There are a lot of old optimizations to reduce segcounts in certain situations, someting that our error-based calculation compensates for anyway. (Smaller radii need fewer segments to meet the maxError condition.) But perhaps more importantly we now surface maxError in the UI and we don't really want to call it "Max deviation except when it's not". 3) We were also clamping the segCount twice: once in the calculation routine and once in most of it's callers. Furthermore, the caller clamping was inconsistent (both in being done and in the clamping value). We now clamp only in the calculation routine. 4) There's no reason to use the correction factors in the 3Dviewer; it's just a visualization and whether the polygonization error is inside or outside the shape isn't really material. 5) The arc-correction-disabling stuff (used for solder mask layer) was somewhat fragile in that it depended on the caller to turn it back on afterwards. It's now only exposed as a RAII object which automatically cleans up when it goes out of scope. 6) There were also bugs in a couple of the polygonization routines where we'd accumulate round-off error in adding up the segments and end up with an overly long last segment (which of course would voilate the error max). This was the cause of the linked bug and also some issues with vias that we had fudged in the past with extra clearance. Fixes https://gitlab.com/kicad/code/kicad/issues/5567
5 years ago
Clean up arc/circle polygonization. 1) For a while now we've been using a calculated seg count from a given maxError, and a correction factor to push the radius out so that all the error is outside the arc/circle. However, the second calculation (which pre-dates the first) is pretty much just the inverse of the first (and yields nothing more than maxError back). This is particularly sub-optimal given the cost of trig functions. 2) There are a lot of old optimizations to reduce segcounts in certain situations, someting that our error-based calculation compensates for anyway. (Smaller radii need fewer segments to meet the maxError condition.) But perhaps more importantly we now surface maxError in the UI and we don't really want to call it "Max deviation except when it's not". 3) We were also clamping the segCount twice: once in the calculation routine and once in most of it's callers. Furthermore, the caller clamping was inconsistent (both in being done and in the clamping value). We now clamp only in the calculation routine. 4) There's no reason to use the correction factors in the 3Dviewer; it's just a visualization and whether the polygonization error is inside or outside the shape isn't really material. 5) The arc-correction-disabling stuff (used for solder mask layer) was somewhat fragile in that it depended on the caller to turn it back on afterwards. It's now only exposed as a RAII object which automatically cleans up when it goes out of scope. 6) There were also bugs in a couple of the polygonization routines where we'd accumulate round-off error in adding up the segments and end up with an overly long last segment (which of course would voilate the error max). This was the cause of the linked bug and also some issues with vias that we had fudged in the past with extra clearance. Fixes https://gitlab.com/kicad/code/kicad/issues/5567
5 years ago
Clean up arc/circle polygonization. 1) For a while now we've been using a calculated seg count from a given maxError, and a correction factor to push the radius out so that all the error is outside the arc/circle. However, the second calculation (which pre-dates the first) is pretty much just the inverse of the first (and yields nothing more than maxError back). This is particularly sub-optimal given the cost of trig functions. 2) There are a lot of old optimizations to reduce segcounts in certain situations, someting that our error-based calculation compensates for anyway. (Smaller radii need fewer segments to meet the maxError condition.) But perhaps more importantly we now surface maxError in the UI and we don't really want to call it "Max deviation except when it's not". 3) We were also clamping the segCount twice: once in the calculation routine and once in most of it's callers. Furthermore, the caller clamping was inconsistent (both in being done and in the clamping value). We now clamp only in the calculation routine. 4) There's no reason to use the correction factors in the 3Dviewer; it's just a visualization and whether the polygonization error is inside or outside the shape isn't really material. 5) The arc-correction-disabling stuff (used for solder mask layer) was somewhat fragile in that it depended on the caller to turn it back on afterwards. It's now only exposed as a RAII object which automatically cleans up when it goes out of scope. 6) There were also bugs in a couple of the polygonization routines where we'd accumulate round-off error in adding up the segments and end up with an overly long last segment (which of course would voilate the error max). This was the cause of the linked bug and also some issues with vias that we had fudged in the past with extra clearance. Fixes https://gitlab.com/kicad/code/kicad/issues/5567
5 years ago
Clean up arc/circle polygonization. 1) For a while now we've been using a calculated seg count from a given maxError, and a correction factor to push the radius out so that all the error is outside the arc/circle. However, the second calculation (which pre-dates the first) is pretty much just the inverse of the first (and yields nothing more than maxError back). This is particularly sub-optimal given the cost of trig functions. 2) There are a lot of old optimizations to reduce segcounts in certain situations, someting that our error-based calculation compensates for anyway. (Smaller radii need fewer segments to meet the maxError condition.) But perhaps more importantly we now surface maxError in the UI and we don't really want to call it "Max deviation except when it's not". 3) We were also clamping the segCount twice: once in the calculation routine and once in most of it's callers. Furthermore, the caller clamping was inconsistent (both in being done and in the clamping value). We now clamp only in the calculation routine. 4) There's no reason to use the correction factors in the 3Dviewer; it's just a visualization and whether the polygonization error is inside or outside the shape isn't really material. 5) The arc-correction-disabling stuff (used for solder mask layer) was somewhat fragile in that it depended on the caller to turn it back on afterwards. It's now only exposed as a RAII object which automatically cleans up when it goes out of scope. 6) There were also bugs in a couple of the polygonization routines where we'd accumulate round-off error in adding up the segments and end up with an overly long last segment (which of course would voilate the error max). This was the cause of the linked bug and also some issues with vias that we had fudged in the past with extra clearance. Fixes https://gitlab.com/kicad/code/kicad/issues/5567
5 years ago
Clean up arc/circle polygonization. 1) For a while now we've been using a calculated seg count from a given maxError, and a correction factor to push the radius out so that all the error is outside the arc/circle. However, the second calculation (which pre-dates the first) is pretty much just the inverse of the first (and yields nothing more than maxError back). This is particularly sub-optimal given the cost of trig functions. 2) There are a lot of old optimizations to reduce segcounts in certain situations, someting that our error-based calculation compensates for anyway. (Smaller radii need fewer segments to meet the maxError condition.) But perhaps more importantly we now surface maxError in the UI and we don't really want to call it "Max deviation except when it's not". 3) We were also clamping the segCount twice: once in the calculation routine and once in most of it's callers. Furthermore, the caller clamping was inconsistent (both in being done and in the clamping value). We now clamp only in the calculation routine. 4) There's no reason to use the correction factors in the 3Dviewer; it's just a visualization and whether the polygonization error is inside or outside the shape isn't really material. 5) The arc-correction-disabling stuff (used for solder mask layer) was somewhat fragile in that it depended on the caller to turn it back on afterwards. It's now only exposed as a RAII object which automatically cleans up when it goes out of scope. 6) There were also bugs in a couple of the polygonization routines where we'd accumulate round-off error in adding up the segments and end up with an overly long last segment (which of course would voilate the error max). This was the cause of the linked bug and also some issues with vias that we had fudged in the past with extra clearance. Fixes https://gitlab.com/kicad/code/kicad/issues/5567
5 years ago
Clean up arc/circle polygonization. 1) For a while now we've been using a calculated seg count from a given maxError, and a correction factor to push the radius out so that all the error is outside the arc/circle. However, the second calculation (which pre-dates the first) is pretty much just the inverse of the first (and yields nothing more than maxError back). This is particularly sub-optimal given the cost of trig functions. 2) There are a lot of old optimizations to reduce segcounts in certain situations, someting that our error-based calculation compensates for anyway. (Smaller radii need fewer segments to meet the maxError condition.) But perhaps more importantly we now surface maxError in the UI and we don't really want to call it "Max deviation except when it's not". 3) We were also clamping the segCount twice: once in the calculation routine and once in most of it's callers. Furthermore, the caller clamping was inconsistent (both in being done and in the clamping value). We now clamp only in the calculation routine. 4) There's no reason to use the correction factors in the 3Dviewer; it's just a visualization and whether the polygonization error is inside or outside the shape isn't really material. 5) The arc-correction-disabling stuff (used for solder mask layer) was somewhat fragile in that it depended on the caller to turn it back on afterwards. It's now only exposed as a RAII object which automatically cleans up when it goes out of scope. 6) There were also bugs in a couple of the polygonization routines where we'd accumulate round-off error in adding up the segments and end up with an overly long last segment (which of course would voilate the error max). This was the cause of the linked bug and also some issues with vias that we had fudged in the past with extra clearance. Fixes https://gitlab.com/kicad/code/kicad/issues/5567
5 years ago
Clean up arc/circle polygonization. 1) For a while now we've been using a calculated seg count from a given maxError, and a correction factor to push the radius out so that all the error is outside the arc/circle. However, the second calculation (which pre-dates the first) is pretty much just the inverse of the first (and yields nothing more than maxError back). This is particularly sub-optimal given the cost of trig functions. 2) There are a lot of old optimizations to reduce segcounts in certain situations, someting that our error-based calculation compensates for anyway. (Smaller radii need fewer segments to meet the maxError condition.) But perhaps more importantly we now surface maxError in the UI and we don't really want to call it "Max deviation except when it's not". 3) We were also clamping the segCount twice: once in the calculation routine and once in most of it's callers. Furthermore, the caller clamping was inconsistent (both in being done and in the clamping value). We now clamp only in the calculation routine. 4) There's no reason to use the correction factors in the 3Dviewer; it's just a visualization and whether the polygonization error is inside or outside the shape isn't really material. 5) The arc-correction-disabling stuff (used for solder mask layer) was somewhat fragile in that it depended on the caller to turn it back on afterwards. It's now only exposed as a RAII object which automatically cleans up when it goes out of scope. 6) There were also bugs in a couple of the polygonization routines where we'd accumulate round-off error in adding up the segments and end up with an overly long last segment (which of course would voilate the error max). This was the cause of the linked bug and also some issues with vias that we had fudged in the past with extra clearance. Fixes https://gitlab.com/kicad/code/kicad/issues/5567
5 years ago
Clean up arc/circle polygonization. 1) For a while now we've been using a calculated seg count from a given maxError, and a correction factor to push the radius out so that all the error is outside the arc/circle. However, the second calculation (which pre-dates the first) is pretty much just the inverse of the first (and yields nothing more than maxError back). This is particularly sub-optimal given the cost of trig functions. 2) There are a lot of old optimizations to reduce segcounts in certain situations, someting that our error-based calculation compensates for anyway. (Smaller radii need fewer segments to meet the maxError condition.) But perhaps more importantly we now surface maxError in the UI and we don't really want to call it "Max deviation except when it's not". 3) We were also clamping the segCount twice: once in the calculation routine and once in most of it's callers. Furthermore, the caller clamping was inconsistent (both in being done and in the clamping value). We now clamp only in the calculation routine. 4) There's no reason to use the correction factors in the 3Dviewer; it's just a visualization and whether the polygonization error is inside or outside the shape isn't really material. 5) The arc-correction-disabling stuff (used for solder mask layer) was somewhat fragile in that it depended on the caller to turn it back on afterwards. It's now only exposed as a RAII object which automatically cleans up when it goes out of scope. 6) There were also bugs in a couple of the polygonization routines where we'd accumulate round-off error in adding up the segments and end up with an overly long last segment (which of course would voilate the error max). This was the cause of the linked bug and also some issues with vias that we had fudged in the past with extra clearance. Fixes https://gitlab.com/kicad/code/kicad/issues/5567
5 years ago
Clean up arc/circle polygonization. 1) For a while now we've been using a calculated seg count from a given maxError, and a correction factor to push the radius out so that all the error is outside the arc/circle. However, the second calculation (which pre-dates the first) is pretty much just the inverse of the first (and yields nothing more than maxError back). This is particularly sub-optimal given the cost of trig functions. 2) There are a lot of old optimizations to reduce segcounts in certain situations, someting that our error-based calculation compensates for anyway. (Smaller radii need fewer segments to meet the maxError condition.) But perhaps more importantly we now surface maxError in the UI and we don't really want to call it "Max deviation except when it's not". 3) We were also clamping the segCount twice: once in the calculation routine and once in most of it's callers. Furthermore, the caller clamping was inconsistent (both in being done and in the clamping value). We now clamp only in the calculation routine. 4) There's no reason to use the correction factors in the 3Dviewer; it's just a visualization and whether the polygonization error is inside or outside the shape isn't really material. 5) The arc-correction-disabling stuff (used for solder mask layer) was somewhat fragile in that it depended on the caller to turn it back on afterwards. It's now only exposed as a RAII object which automatically cleans up when it goes out of scope. 6) There were also bugs in a couple of the polygonization routines where we'd accumulate round-off error in adding up the segments and end up with an overly long last segment (which of course would voilate the error max). This was the cause of the linked bug and also some issues with vias that we had fudged in the past with extra clearance. Fixes https://gitlab.com/kicad/code/kicad/issues/5567
5 years ago
Clean up arc/circle polygonization. 1) For a while now we've been using a calculated seg count from a given maxError, and a correction factor to push the radius out so that all the error is outside the arc/circle. However, the second calculation (which pre-dates the first) is pretty much just the inverse of the first (and yields nothing more than maxError back). This is particularly sub-optimal given the cost of trig functions. 2) There are a lot of old optimizations to reduce segcounts in certain situations, someting that our error-based calculation compensates for anyway. (Smaller radii need fewer segments to meet the maxError condition.) But perhaps more importantly we now surface maxError in the UI and we don't really want to call it "Max deviation except when it's not". 3) We were also clamping the segCount twice: once in the calculation routine and once in most of it's callers. Furthermore, the caller clamping was inconsistent (both in being done and in the clamping value). We now clamp only in the calculation routine. 4) There's no reason to use the correction factors in the 3Dviewer; it's just a visualization and whether the polygonization error is inside or outside the shape isn't really material. 5) The arc-correction-disabling stuff (used for solder mask layer) was somewhat fragile in that it depended on the caller to turn it back on afterwards. It's now only exposed as a RAII object which automatically cleans up when it goes out of scope. 6) There were also bugs in a couple of the polygonization routines where we'd accumulate round-off error in adding up the segments and end up with an overly long last segment (which of course would voilate the error max). This was the cause of the linked bug and also some issues with vias that we had fudged in the past with extra clearance. Fixes https://gitlab.com/kicad/code/kicad/issues/5567
5 years ago
Clean up arc/circle polygonization. 1) For a while now we've been using a calculated seg count from a given maxError, and a correction factor to push the radius out so that all the error is outside the arc/circle. However, the second calculation (which pre-dates the first) is pretty much just the inverse of the first (and yields nothing more than maxError back). This is particularly sub-optimal given the cost of trig functions. 2) There are a lot of old optimizations to reduce segcounts in certain situations, someting that our error-based calculation compensates for anyway. (Smaller radii need fewer segments to meet the maxError condition.) But perhaps more importantly we now surface maxError in the UI and we don't really want to call it "Max deviation except when it's not". 3) We were also clamping the segCount twice: once in the calculation routine and once in most of it's callers. Furthermore, the caller clamping was inconsistent (both in being done and in the clamping value). We now clamp only in the calculation routine. 4) There's no reason to use the correction factors in the 3Dviewer; it's just a visualization and whether the polygonization error is inside or outside the shape isn't really material. 5) The arc-correction-disabling stuff (used for solder mask layer) was somewhat fragile in that it depended on the caller to turn it back on afterwards. It's now only exposed as a RAII object which automatically cleans up when it goes out of scope. 6) There were also bugs in a couple of the polygonization routines where we'd accumulate round-off error in adding up the segments and end up with an overly long last segment (which of course would voilate the error max). This was the cause of the linked bug and also some issues with vias that we had fudged in the past with extra clearance. Fixes https://gitlab.com/kicad/code/kicad/issues/5567
5 years ago
Clean up arc/circle polygonization. 1) For a while now we've been using a calculated seg count from a given maxError, and a correction factor to push the radius out so that all the error is outside the arc/circle. However, the second calculation (which pre-dates the first) is pretty much just the inverse of the first (and yields nothing more than maxError back). This is particularly sub-optimal given the cost of trig functions. 2) There are a lot of old optimizations to reduce segcounts in certain situations, someting that our error-based calculation compensates for anyway. (Smaller radii need fewer segments to meet the maxError condition.) But perhaps more importantly we now surface maxError in the UI and we don't really want to call it "Max deviation except when it's not". 3) We were also clamping the segCount twice: once in the calculation routine and once in most of it's callers. Furthermore, the caller clamping was inconsistent (both in being done and in the clamping value). We now clamp only in the calculation routine. 4) There's no reason to use the correction factors in the 3Dviewer; it's just a visualization and whether the polygonization error is inside or outside the shape isn't really material. 5) The arc-correction-disabling stuff (used for solder mask layer) was somewhat fragile in that it depended on the caller to turn it back on afterwards. It's now only exposed as a RAII object which automatically cleans up when it goes out of scope. 6) There were also bugs in a couple of the polygonization routines where we'd accumulate round-off error in adding up the segments and end up with an overly long last segment (which of course would voilate the error max). This was the cause of the linked bug and also some issues with vias that we had fudged in the past with extra clearance. Fixes https://gitlab.com/kicad/code/kicad/issues/5567
5 years ago
Clean up arc/circle polygonization. 1) For a while now we've been using a calculated seg count from a given maxError, and a correction factor to push the radius out so that all the error is outside the arc/circle. However, the second calculation (which pre-dates the first) is pretty much just the inverse of the first (and yields nothing more than maxError back). This is particularly sub-optimal given the cost of trig functions. 2) There are a lot of old optimizations to reduce segcounts in certain situations, someting that our error-based calculation compensates for anyway. (Smaller radii need fewer segments to meet the maxError condition.) But perhaps more importantly we now surface maxError in the UI and we don't really want to call it "Max deviation except when it's not". 3) We were also clamping the segCount twice: once in the calculation routine and once in most of it's callers. Furthermore, the caller clamping was inconsistent (both in being done and in the clamping value). We now clamp only in the calculation routine. 4) There's no reason to use the correction factors in the 3Dviewer; it's just a visualization and whether the polygonization error is inside or outside the shape isn't really material. 5) The arc-correction-disabling stuff (used for solder mask layer) was somewhat fragile in that it depended on the caller to turn it back on afterwards. It's now only exposed as a RAII object which automatically cleans up when it goes out of scope. 6) There were also bugs in a couple of the polygonization routines where we'd accumulate round-off error in adding up the segments and end up with an overly long last segment (which of course would voilate the error max). This was the cause of the linked bug and also some issues with vias that we had fudged in the past with extra clearance. Fixes https://gitlab.com/kicad/code/kicad/issues/5567
5 years ago
Clean up arc/circle polygonization. 1) For a while now we've been using a calculated seg count from a given maxError, and a correction factor to push the radius out so that all the error is outside the arc/circle. However, the second calculation (which pre-dates the first) is pretty much just the inverse of the first (and yields nothing more than maxError back). This is particularly sub-optimal given the cost of trig functions. 2) There are a lot of old optimizations to reduce segcounts in certain situations, someting that our error-based calculation compensates for anyway. (Smaller radii need fewer segments to meet the maxError condition.) But perhaps more importantly we now surface maxError in the UI and we don't really want to call it "Max deviation except when it's not". 3) We were also clamping the segCount twice: once in the calculation routine and once in most of it's callers. Furthermore, the caller clamping was inconsistent (both in being done and in the clamping value). We now clamp only in the calculation routine. 4) There's no reason to use the correction factors in the 3Dviewer; it's just a visualization and whether the polygonization error is inside or outside the shape isn't really material. 5) The arc-correction-disabling stuff (used for solder mask layer) was somewhat fragile in that it depended on the caller to turn it back on afterwards. It's now only exposed as a RAII object which automatically cleans up when it goes out of scope. 6) There were also bugs in a couple of the polygonization routines where we'd accumulate round-off error in adding up the segments and end up with an overly long last segment (which of course would voilate the error max). This was the cause of the linked bug and also some issues with vias that we had fudged in the past with extra clearance. Fixes https://gitlab.com/kicad/code/kicad/issues/5567
5 years ago
Clean up arc/circle polygonization. 1) For a while now we've been using a calculated seg count from a given maxError, and a correction factor to push the radius out so that all the error is outside the arc/circle. However, the second calculation (which pre-dates the first) is pretty much just the inverse of the first (and yields nothing more than maxError back). This is particularly sub-optimal given the cost of trig functions. 2) There are a lot of old optimizations to reduce segcounts in certain situations, someting that our error-based calculation compensates for anyway. (Smaller radii need fewer segments to meet the maxError condition.) But perhaps more importantly we now surface maxError in the UI and we don't really want to call it "Max deviation except when it's not". 3) We were also clamping the segCount twice: once in the calculation routine and once in most of it's callers. Furthermore, the caller clamping was inconsistent (both in being done and in the clamping value). We now clamp only in the calculation routine. 4) There's no reason to use the correction factors in the 3Dviewer; it's just a visualization and whether the polygonization error is inside or outside the shape isn't really material. 5) The arc-correction-disabling stuff (used for solder mask layer) was somewhat fragile in that it depended on the caller to turn it back on afterwards. It's now only exposed as a RAII object which automatically cleans up when it goes out of scope. 6) There were also bugs in a couple of the polygonization routines where we'd accumulate round-off error in adding up the segments and end up with an overly long last segment (which of course would voilate the error max). This was the cause of the linked bug and also some issues with vias that we had fudged in the past with extra clearance. Fixes https://gitlab.com/kicad/code/kicad/issues/5567
5 years ago
Clean up arc/circle polygonization. 1) For a while now we've been using a calculated seg count from a given maxError, and a correction factor to push the radius out so that all the error is outside the arc/circle. However, the second calculation (which pre-dates the first) is pretty much just the inverse of the first (and yields nothing more than maxError back). This is particularly sub-optimal given the cost of trig functions. 2) There are a lot of old optimizations to reduce segcounts in certain situations, someting that our error-based calculation compensates for anyway. (Smaller radii need fewer segments to meet the maxError condition.) But perhaps more importantly we now surface maxError in the UI and we don't really want to call it "Max deviation except when it's not". 3) We were also clamping the segCount twice: once in the calculation routine and once in most of it's callers. Furthermore, the caller clamping was inconsistent (both in being done and in the clamping value). We now clamp only in the calculation routine. 4) There's no reason to use the correction factors in the 3Dviewer; it's just a visualization and whether the polygonization error is inside or outside the shape isn't really material. 5) The arc-correction-disabling stuff (used for solder mask layer) was somewhat fragile in that it depended on the caller to turn it back on afterwards. It's now only exposed as a RAII object which automatically cleans up when it goes out of scope. 6) There were also bugs in a couple of the polygonization routines where we'd accumulate round-off error in adding up the segments and end up with an overly long last segment (which of course would voilate the error max). This was the cause of the linked bug and also some issues with vias that we had fudged in the past with extra clearance. Fixes https://gitlab.com/kicad/code/kicad/issues/5567
5 years ago
Clean up arc/circle polygonization. 1) For a while now we've been using a calculated seg count from a given maxError, and a correction factor to push the radius out so that all the error is outside the arc/circle. However, the second calculation (which pre-dates the first) is pretty much just the inverse of the first (and yields nothing more than maxError back). This is particularly sub-optimal given the cost of trig functions. 2) There are a lot of old optimizations to reduce segcounts in certain situations, someting that our error-based calculation compensates for anyway. (Smaller radii need fewer segments to meet the maxError condition.) But perhaps more importantly we now surface maxError in the UI and we don't really want to call it "Max deviation except when it's not". 3) We were also clamping the segCount twice: once in the calculation routine and once in most of it's callers. Furthermore, the caller clamping was inconsistent (both in being done and in the clamping value). We now clamp only in the calculation routine. 4) There's no reason to use the correction factors in the 3Dviewer; it's just a visualization and whether the polygonization error is inside or outside the shape isn't really material. 5) The arc-correction-disabling stuff (used for solder mask layer) was somewhat fragile in that it depended on the caller to turn it back on afterwards. It's now only exposed as a RAII object which automatically cleans up when it goes out of scope. 6) There were also bugs in a couple of the polygonization routines where we'd accumulate round-off error in adding up the segments and end up with an overly long last segment (which of course would voilate the error max). This was the cause of the linked bug and also some issues with vias that we had fudged in the past with extra clearance. Fixes https://gitlab.com/kicad/code/kicad/issues/5567
5 years ago
Clean up arc/circle polygonization. 1) For a while now we've been using a calculated seg count from a given maxError, and a correction factor to push the radius out so that all the error is outside the arc/circle. However, the second calculation (which pre-dates the first) is pretty much just the inverse of the first (and yields nothing more than maxError back). This is particularly sub-optimal given the cost of trig functions. 2) There are a lot of old optimizations to reduce segcounts in certain situations, someting that our error-based calculation compensates for anyway. (Smaller radii need fewer segments to meet the maxError condition.) But perhaps more importantly we now surface maxError in the UI and we don't really want to call it "Max deviation except when it's not". 3) We were also clamping the segCount twice: once in the calculation routine and once in most of it's callers. Furthermore, the caller clamping was inconsistent (both in being done and in the clamping value). We now clamp only in the calculation routine. 4) There's no reason to use the correction factors in the 3Dviewer; it's just a visualization and whether the polygonization error is inside or outside the shape isn't really material. 5) The arc-correction-disabling stuff (used for solder mask layer) was somewhat fragile in that it depended on the caller to turn it back on afterwards. It's now only exposed as a RAII object which automatically cleans up when it goes out of scope. 6) There were also bugs in a couple of the polygonization routines where we'd accumulate round-off error in adding up the segments and end up with an overly long last segment (which of course would voilate the error max). This was the cause of the linked bug and also some issues with vias that we had fudged in the past with extra clearance. Fixes https://gitlab.com/kicad/code/kicad/issues/5567
5 years ago
Clean up arc/circle polygonization. 1) For a while now we've been using a calculated seg count from a given maxError, and a correction factor to push the radius out so that all the error is outside the arc/circle. However, the second calculation (which pre-dates the first) is pretty much just the inverse of the first (and yields nothing more than maxError back). This is particularly sub-optimal given the cost of trig functions. 2) There are a lot of old optimizations to reduce segcounts in certain situations, someting that our error-based calculation compensates for anyway. (Smaller radii need fewer segments to meet the maxError condition.) But perhaps more importantly we now surface maxError in the UI and we don't really want to call it "Max deviation except when it's not". 3) We were also clamping the segCount twice: once in the calculation routine and once in most of it's callers. Furthermore, the caller clamping was inconsistent (both in being done and in the clamping value). We now clamp only in the calculation routine. 4) There's no reason to use the correction factors in the 3Dviewer; it's just a visualization and whether the polygonization error is inside or outside the shape isn't really material. 5) The arc-correction-disabling stuff (used for solder mask layer) was somewhat fragile in that it depended on the caller to turn it back on afterwards. It's now only exposed as a RAII object which automatically cleans up when it goes out of scope. 6) There were also bugs in a couple of the polygonization routines where we'd accumulate round-off error in adding up the segments and end up with an overly long last segment (which of course would voilate the error max). This was the cause of the linked bug and also some issues with vias that we had fudged in the past with extra clearance. Fixes https://gitlab.com/kicad/code/kicad/issues/5567
5 years ago
Clean up arc/circle polygonization. 1) For a while now we've been using a calculated seg count from a given maxError, and a correction factor to push the radius out so that all the error is outside the arc/circle. However, the second calculation (which pre-dates the first) is pretty much just the inverse of the first (and yields nothing more than maxError back). This is particularly sub-optimal given the cost of trig functions. 2) There are a lot of old optimizations to reduce segcounts in certain situations, someting that our error-based calculation compensates for anyway. (Smaller radii need fewer segments to meet the maxError condition.) But perhaps more importantly we now surface maxError in the UI and we don't really want to call it "Max deviation except when it's not". 3) We were also clamping the segCount twice: once in the calculation routine and once in most of it's callers. Furthermore, the caller clamping was inconsistent (both in being done and in the clamping value). We now clamp only in the calculation routine. 4) There's no reason to use the correction factors in the 3Dviewer; it's just a visualization and whether the polygonization error is inside or outside the shape isn't really material. 5) The arc-correction-disabling stuff (used for solder mask layer) was somewhat fragile in that it depended on the caller to turn it back on afterwards. It's now only exposed as a RAII object which automatically cleans up when it goes out of scope. 6) There were also bugs in a couple of the polygonization routines where we'd accumulate round-off error in adding up the segments and end up with an overly long last segment (which of course would voilate the error max). This was the cause of the linked bug and also some issues with vias that we had fudged in the past with extra clearance. Fixes https://gitlab.com/kicad/code/kicad/issues/5567
5 years ago
Clean up arc/circle polygonization. 1) For a while now we've been using a calculated seg count from a given maxError, and a correction factor to push the radius out so that all the error is outside the arc/circle. However, the second calculation (which pre-dates the first) is pretty much just the inverse of the first (and yields nothing more than maxError back). This is particularly sub-optimal given the cost of trig functions. 2) There are a lot of old optimizations to reduce segcounts in certain situations, someting that our error-based calculation compensates for anyway. (Smaller radii need fewer segments to meet the maxError condition.) But perhaps more importantly we now surface maxError in the UI and we don't really want to call it "Max deviation except when it's not". 3) We were also clamping the segCount twice: once in the calculation routine and once in most of it's callers. Furthermore, the caller clamping was inconsistent (both in being done and in the clamping value). We now clamp only in the calculation routine. 4) There's no reason to use the correction factors in the 3Dviewer; it's just a visualization and whether the polygonization error is inside or outside the shape isn't really material. 5) The arc-correction-disabling stuff (used for solder mask layer) was somewhat fragile in that it depended on the caller to turn it back on afterwards. It's now only exposed as a RAII object which automatically cleans up when it goes out of scope. 6) There were also bugs in a couple of the polygonization routines where we'd accumulate round-off error in adding up the segments and end up with an overly long last segment (which of course would voilate the error max). This was the cause of the linked bug and also some issues with vias that we had fudged in the past with extra clearance. Fixes https://gitlab.com/kicad/code/kicad/issues/5567
5 years ago
  1. /**
  2. * @file plot_board_layers.cpp
  3. * @brief Functions to plot one board layer (silkscreen layers or other layers).
  4. * Silkscreen layers have specific requirement for pads (not filled) and texts
  5. * (with option to remove them from some copper areas (pads...)
  6. */
  7. /*
  8. * This program source code file is part of KiCad, a free EDA CAD application.
  9. *
  10. * Copyright (C) 1992-2020 KiCad Developers, see AUTHORS.txt for contributors.
  11. *
  12. * This program is free software; you can redistribute it and/or
  13. * modify it under the terms of the GNU General Public License
  14. * as published by the Free Software Foundation; either version 2
  15. * of the License, or (at your option) any later version.
  16. *
  17. * This program is distributed in the hope that it will be useful,
  18. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  19. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  20. * GNU General Public License for more details.
  21. *
  22. * You should have received a copy of the GNU General Public License
  23. * along with this program; if not, you may find one here:
  24. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  25. * or you may search the http://www.gnu.org website for the version 2 license,
  26. * or you may write to the Free Software Foundation, Inc.,
  27. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  28. */
  29. #include <eda_item.h>
  30. #include <geometry/geometry_utils.h>
  31. #include <geometry/shape_segment.h>
  32. #include <pcb_base_frame.h>
  33. #include <math/util.h> // for KiROUND
  34. #include <class_board.h>
  35. #include <class_module.h>
  36. #include <class_track.h>
  37. #include <fp_shape.h>
  38. #include <pcb_text.h>
  39. #include <class_zone.h>
  40. #include <pcb_shape.h>
  41. #include <class_pcb_target.h>
  42. #include <class_dimension.h>
  43. #include <pcbplot.h>
  44. #include <pcb_painter.h>
  45. #include <gbr_metadata.h>
  46. /*
  47. * Plot a solder mask layer. Solder mask layers have a minimum thickness value and cannot be
  48. * drawn like standard layers, unless the minimum thickness is 0.
  49. */
  50. static void PlotSolderMaskLayer( BOARD *aBoard, PLOTTER* aPlotter, LSET aLayerMask,
  51. const PCB_PLOT_PARAMS& aPlotOpt, int aMinThickness );
  52. void PlotOneBoardLayer( BOARD *aBoard, PLOTTER* aPlotter, PCB_LAYER_ID aLayer,
  53. const PCB_PLOT_PARAMS& aPlotOpt )
  54. {
  55. PCB_PLOT_PARAMS plotOpt = aPlotOpt;
  56. int soldermask_min_thickness = aBoard->GetDesignSettings().m_SolderMaskMinWidth;
  57. // Set a default color and the text mode for this layer
  58. aPlotter->SetColor( aPlotOpt.GetColor() );
  59. aPlotter->SetTextMode( aPlotOpt.GetTextMode() );
  60. // Specify that the contents of the "Edges Pcb" layer are to be plotted in addition to the
  61. // contents of the currently specified layer.
  62. LSET layer_mask( aLayer );
  63. if( !aPlotOpt.GetExcludeEdgeLayer() )
  64. layer_mask.set( Edge_Cuts );
  65. if( IsCopperLayer( aLayer ) )
  66. {
  67. // Skip NPTH pads on copper layers ( only if hole size == pad size ):
  68. // Drill mark will be plotted if drill mark is SMALL_DRILL_SHAPE or FULL_DRILL_SHAPE
  69. if( plotOpt.GetFormat() == PLOT_FORMAT::DXF )
  70. {
  71. plotOpt.SetSkipPlotNPTH_Pads( false );
  72. PlotLayerOutlines( aBoard, aPlotter, layer_mask, plotOpt );
  73. }
  74. else
  75. {
  76. plotOpt.SetSkipPlotNPTH_Pads( true );
  77. PlotStandardLayer( aBoard, aPlotter, layer_mask, plotOpt );
  78. }
  79. }
  80. else
  81. {
  82. switch( aLayer )
  83. {
  84. case B_Mask:
  85. case F_Mask:
  86. plotOpt.SetSkipPlotNPTH_Pads( false );
  87. // Disable plot pad holes
  88. plotOpt.SetDrillMarksType( PCB_PLOT_PARAMS::NO_DRILL_SHAPE );
  89. // Plot solder mask:
  90. if( soldermask_min_thickness == 0 )
  91. {
  92. if( plotOpt.GetFormat() == PLOT_FORMAT::DXF )
  93. PlotLayerOutlines( aBoard, aPlotter, layer_mask, plotOpt );
  94. else
  95. PlotStandardLayer( aBoard, aPlotter, layer_mask, plotOpt );
  96. }
  97. else
  98. PlotSolderMaskLayer( aBoard, aPlotter, layer_mask, plotOpt,
  99. soldermask_min_thickness );
  100. break;
  101. case B_Adhes:
  102. case F_Adhes:
  103. case B_Paste:
  104. case F_Paste:
  105. plotOpt.SetSkipPlotNPTH_Pads( false );
  106. // Disable plot pad holes
  107. plotOpt.SetDrillMarksType( PCB_PLOT_PARAMS::NO_DRILL_SHAPE );
  108. if( plotOpt.GetFormat() == PLOT_FORMAT::DXF )
  109. PlotLayerOutlines( aBoard, aPlotter, layer_mask, plotOpt );
  110. else
  111. PlotStandardLayer( aBoard, aPlotter, layer_mask, plotOpt );
  112. break;
  113. case F_SilkS:
  114. case B_SilkS:
  115. if( plotOpt.GetFormat() == PLOT_FORMAT::DXF && plotOpt.GetDXFPlotPolygonMode() )
  116. // PlotLayerOutlines() is designed only for DXF plotters.
  117. // and must not be used for other plot formats
  118. PlotLayerOutlines( aBoard, aPlotter, layer_mask, plotOpt );
  119. else
  120. PlotStandardLayer( aBoard, aPlotter, layer_mask, plotOpt );
  121. // Gerber: Subtract soldermask from silkscreen if enabled
  122. if( aPlotter->GetPlotterType() == PLOT_FORMAT::GERBER
  123. && plotOpt.GetSubtractMaskFromSilk() )
  124. {
  125. if( aLayer == F_SilkS )
  126. layer_mask = LSET( F_Mask );
  127. else
  128. layer_mask = LSET( B_Mask );
  129. // Create the mask to subtract by creating a negative layer polarity
  130. aPlotter->SetLayerPolarity( false );
  131. // Disable plot pad holes
  132. plotOpt.SetDrillMarksType( PCB_PLOT_PARAMS::NO_DRILL_SHAPE );
  133. // Plot the mask
  134. PlotStandardLayer( aBoard, aPlotter, layer_mask, plotOpt );
  135. }
  136. break;
  137. // These layers are plotted like silk screen layers.
  138. // Mainly, pads on these layers are not filled.
  139. // This is not necessary the best choice.
  140. case Dwgs_User:
  141. case Cmts_User:
  142. case Eco1_User:
  143. case Eco2_User:
  144. case Edge_Cuts:
  145. case Margin:
  146. case F_CrtYd:
  147. case B_CrtYd:
  148. case F_Fab:
  149. case B_Fab:
  150. plotOpt.SetSkipPlotNPTH_Pads( false );
  151. plotOpt.SetDrillMarksType( PCB_PLOT_PARAMS::NO_DRILL_SHAPE );
  152. if( plotOpt.GetFormat() == PLOT_FORMAT::DXF && plotOpt.GetDXFPlotPolygonMode() )
  153. // PlotLayerOutlines() is designed only for DXF plotters.
  154. // and must not be used for other plot formats
  155. PlotLayerOutlines( aBoard, aPlotter, layer_mask, plotOpt );
  156. else
  157. PlotStandardLayer( aBoard, aPlotter, layer_mask, plotOpt );
  158. break;
  159. default:
  160. plotOpt.SetSkipPlotNPTH_Pads( false );
  161. plotOpt.SetDrillMarksType( PCB_PLOT_PARAMS::NO_DRILL_SHAPE );
  162. if( plotOpt.GetFormat() == PLOT_FORMAT::DXF && plotOpt.GetDXFPlotPolygonMode() )
  163. // PlotLayerOutlines() is designed only for DXF plotters.
  164. // and must not be used for other plot formats
  165. PlotLayerOutlines( aBoard, aPlotter, layer_mask, plotOpt );
  166. else
  167. PlotStandardLayer( aBoard, aPlotter, layer_mask, plotOpt );
  168. break;
  169. }
  170. }
  171. }
  172. /*
  173. * Plot a copper layer or mask.
  174. * Silk screen layers are not plotted here.
  175. */
  176. void PlotStandardLayer( BOARD *aBoard, PLOTTER* aPlotter, LSET aLayerMask,
  177. const PCB_PLOT_PARAMS& aPlotOpt )
  178. {
  179. BRDITEMS_PLOTTER itemplotter( aPlotter, aBoard, aPlotOpt );
  180. itemplotter.SetLayerSet( aLayerMask );
  181. OUTLINE_MODE plotMode = aPlotOpt.GetPlotMode();
  182. bool onCopperLayer = ( LSET::AllCuMask() & aLayerMask ).any();
  183. bool onSolderMaskLayer = ( LSET( 2, F_Mask, B_Mask ) & aLayerMask ).any();
  184. bool onSolderPasteLayer = ( LSET( 2, F_Paste, B_Paste ) & aLayerMask ).any();
  185. bool onFrontFab = ( LSET( F_Fab ) & aLayerMask ).any();
  186. bool onBackFab = ( LSET( B_Fab ) & aLayerMask ).any();
  187. bool sketchPads = ( onFrontFab || onBackFab ) && aPlotOpt.GetSketchPadsOnFabLayers();
  188. // Plot edge layer and graphic items
  189. itemplotter.PlotBoardGraphicItems();
  190. // Draw footprint texts:
  191. for( MODULE* module : aBoard->Modules() )
  192. itemplotter.PlotFootprintTextItems( module );
  193. // Draw footprint other graphic items:
  194. for( MODULE* module : aBoard->Modules() )
  195. itemplotter.PlotFootprintGraphicItems( module );
  196. // Plot footprint pads
  197. for( MODULE* module : aBoard->Modules() )
  198. {
  199. aPlotter->StartBlock( NULL );
  200. for( D_PAD* pad : module->Pads() )
  201. {
  202. OUTLINE_MODE padPlotMode = plotMode;
  203. if( !( pad->GetLayerSet() & aLayerMask ).any() )
  204. {
  205. if( sketchPads &&
  206. ( ( onFrontFab && pad->GetLayerSet().Contains( F_Cu ) ) ||
  207. ( onBackFab && pad->GetLayerSet().Contains( B_Cu ) ) ) )
  208. padPlotMode = SKETCH;
  209. else
  210. continue;
  211. }
  212. /// pads not connected to copper are optionally not drawn
  213. if( onCopperLayer && !pad->FlashLayer( aLayerMask ) )
  214. continue;
  215. COLOR4D color = COLOR4D::BLACK;
  216. if( pad->GetLayerSet()[B_Cu] )
  217. color = aPlotOpt.ColorSettings()->GetColor( LAYER_PAD_BK );
  218. if( pad->GetLayerSet()[F_Cu] )
  219. color = color.LegacyMix( aPlotOpt.ColorSettings()->GetColor( LAYER_PAD_FR ) );
  220. if( sketchPads && aLayerMask[F_Fab] )
  221. color = aPlotOpt.ColorSettings()->GetColor( F_Fab );
  222. else if( sketchPads && aLayerMask[B_Fab] )
  223. color = aPlotOpt.ColorSettings()->GetColor( B_Fab );
  224. wxSize margin;
  225. int width_adj = 0;
  226. if( onCopperLayer )
  227. width_adj = itemplotter.getFineWidthAdj();
  228. if( onSolderMaskLayer )
  229. margin.x = margin.y = pad->GetSolderMaskMargin();
  230. if( onSolderPasteLayer )
  231. margin = pad->GetSolderPasteMargin();
  232. // Now offset the pad size by margin + width_adj
  233. wxSize padPlotsSize = pad->GetSize() + margin * 2 + wxSize( width_adj, width_adj );
  234. // Store these parameters that can be modified to plot inflated/deflated pads shape
  235. PAD_SHAPE_T padShape = pad->GetShape();
  236. wxSize padSize = pad->GetSize();
  237. wxSize padDelta = pad->GetDelta(); // has meaning only for trapezoidal pads
  238. double padCornerRadius = pad->GetRoundRectCornerRadius();
  239. // Don't draw a null size item :
  240. if( padPlotsSize.x <= 0 || padPlotsSize.y <= 0 )
  241. continue;
  242. switch( pad->GetShape() )
  243. {
  244. case PAD_SHAPE_CIRCLE:
  245. case PAD_SHAPE_OVAL:
  246. pad->SetSize( padPlotsSize );
  247. if( aPlotOpt.GetSkipPlotNPTH_Pads() &&
  248. ( aPlotOpt.GetDrillMarksType() == PCB_PLOT_PARAMS::NO_DRILL_SHAPE ) &&
  249. ( pad->GetSize() == pad->GetDrillSize() ) &&
  250. ( pad->GetAttribute() == PAD_ATTRIB_NPTH ) )
  251. break;
  252. itemplotter.PlotPad( pad, color, padPlotMode );
  253. break;
  254. case PAD_SHAPE_RECT:
  255. pad->SetSize( padPlotsSize );
  256. if( margin.x > 0 )
  257. {
  258. pad->SetShape( PAD_SHAPE_ROUNDRECT );
  259. pad->SetRoundRectCornerRadius( margin.x );
  260. }
  261. itemplotter.PlotPad( pad, color, padPlotMode );
  262. break;
  263. case PAD_SHAPE_TRAPEZOID:
  264. {
  265. wxSize scale( padPlotsSize.x / padSize.x, padPlotsSize.y / padSize.y );
  266. pad->SetDelta( wxSize( padDelta.x * scale.x, padDelta.y * scale.y ) );
  267. pad->SetSize( padPlotsSize );
  268. itemplotter.PlotPad( pad, color, padPlotMode );
  269. }
  270. break;
  271. case PAD_SHAPE_ROUNDRECT:
  272. case PAD_SHAPE_CHAMFERED_RECT:
  273. // Chamfer and rounding are stored as a percent and so don't need scaling
  274. pad->SetSize( padPlotsSize );
  275. itemplotter.PlotPad( pad, color, padPlotMode );
  276. break;
  277. case PAD_SHAPE_CUSTOM:
  278. {
  279. // inflate/deflate a custom shape is a bit complex.
  280. // so build a similar pad shape, and inflate/deflate the polygonal shape
  281. D_PAD dummy( *pad );
  282. SHAPE_POLY_SET shape;
  283. pad->MergePrimitivesAsPolygon( &shape, UNDEFINED_LAYER );
  284. // Shape polygon can have holes so use InflateWithLinkedHoles(), not Inflate()
  285. // which can create bad shapes if margin.x is < 0
  286. int maxError = aBoard->GetDesignSettings().m_MaxError;
  287. int numSegs = GetArcToSegmentCount( margin.x, maxError, 360.0 );
  288. shape.InflateWithLinkedHoles( margin.x, numSegs, SHAPE_POLY_SET::PM_FAST );
  289. dummy.DeletePrimitivesList();
  290. dummy.AddPrimitivePoly( shape, 0 );
  291. // Be sure the anchor pad is not bigger than the deflated shape because this
  292. // anchor will be added to the pad shape when plotting the pad. So now the
  293. // polygonal shape is built, we can clamp the anchor size
  294. if( margin.x < 0 ) // we expect margin.x = margin.y for custom pads
  295. dummy.SetSize( padPlotsSize );
  296. itemplotter.PlotPad( &dummy, color, padPlotMode );
  297. }
  298. break;
  299. }
  300. // Restore the pad parameters modified by the plot code
  301. pad->SetSize( padSize );
  302. pad->SetDelta( padDelta );
  303. pad->SetShape( padShape );
  304. pad->SetRoundRectCornerRadius( padCornerRadius );
  305. }
  306. aPlotter->EndBlock( NULL );
  307. }
  308. // Plot vias on copper layers, and if aPlotOpt.GetPlotViaOnMaskLayer() is true,
  309. // plot them on solder mask
  310. GBR_METADATA gbr_metadata;
  311. bool isOnCopperLayer = ( aLayerMask & LSET::AllCuMask() ).any();
  312. if( isOnCopperLayer )
  313. {
  314. gbr_metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_VIAPAD );
  315. gbr_metadata.SetNetAttribType( GBR_NETLIST_METADATA::GBR_NETINFO_NET );
  316. }
  317. aPlotter->StartBlock( NULL );
  318. for( TRACK* track : aBoard->Tracks() )
  319. {
  320. const VIA* via = dyn_cast<const VIA*>( track );
  321. if( !via )
  322. continue;
  323. // vias are not plotted if not on selected layer, but if layer is SOLDERMASK_LAYER_BACK
  324. // or SOLDERMASK_LAYER_FRONT, vias are drawn only if they are on the corresponding
  325. // external copper layer
  326. LSET via_mask_layer = via->GetLayerSet();
  327. if( aPlotOpt.GetPlotViaOnMaskLayer() )
  328. {
  329. if( via_mask_layer[B_Cu] )
  330. via_mask_layer.set( B_Mask );
  331. if( via_mask_layer[F_Cu] )
  332. via_mask_layer.set( F_Mask );
  333. }
  334. if( !( via_mask_layer & aLayerMask ).any() )
  335. continue;
  336. int via_margin = 0;
  337. double width_adj = 0;
  338. // If the current layer is a solder mask, use the global mask clearance for vias
  339. if( aLayerMask[B_Mask] || aLayerMask[F_Mask] )
  340. via_margin = aBoard->GetDesignSettings().m_SolderMaskMargin;
  341. if( ( aLayerMask & LSET::AllCuMask() ).any() )
  342. width_adj = itemplotter.getFineWidthAdj();
  343. int diameter = via->GetWidth() + 2 * via_margin + width_adj;
  344. /// Vias not connected to copper are optionally not drawn
  345. if( onCopperLayer && !via->FlashLayer( aLayerMask ) )
  346. continue;
  347. // Don't draw a null size item :
  348. if( diameter <= 0 )
  349. continue;
  350. // Some vias can be not connected (no net).
  351. // Set the m_NotInNet for these vias to force a empty net name in gerber file
  352. gbr_metadata.m_NetlistMetadata.m_NotInNet = via->GetNetname().IsEmpty();
  353. gbr_metadata.SetNetName( via->GetNetname() );
  354. COLOR4D color = aPlotOpt.ColorSettings()->GetColor(
  355. LAYER_VIAS + static_cast<int>( via->GetViaType() ) );
  356. // Set plot color (change WHITE to LIGHTGRAY because the white items are not seen on a
  357. // white paper or screen
  358. aPlotter->SetColor( color != WHITE ? color : LIGHTGRAY );
  359. aPlotter->FlashPadCircle( via->GetStart(), diameter, plotMode, &gbr_metadata );
  360. }
  361. aPlotter->EndBlock( NULL );
  362. aPlotter->StartBlock( NULL );
  363. gbr_metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_CONDUCTOR );
  364. // Plot tracks (not vias) :
  365. for( TRACK* track : aBoard->Tracks() )
  366. {
  367. if( track->Type() == PCB_VIA_T )
  368. continue;
  369. if( !aLayerMask[track->GetLayer()] )
  370. continue;
  371. // Some track segments can be not connected (no net).
  372. // Set the m_NotInNet for these segments to force a empty net name in gerber file
  373. gbr_metadata.m_NetlistMetadata.m_NotInNet = track->GetNetname().IsEmpty();
  374. gbr_metadata.SetNetName( track->GetNetname() );
  375. int width = track->GetWidth() + itemplotter.getFineWidthAdj();
  376. aPlotter->SetColor( itemplotter.getColor( track->GetLayer() ) );
  377. if( track->Type() == PCB_ARC_T )
  378. {
  379. ARC* arc = static_cast<ARC*>( track );
  380. VECTOR2D center( arc->GetCenter() );
  381. int radius = arc->GetRadius();
  382. double start_angle = arc->GetArcAngleStart();
  383. double end_angle = start_angle + arc->GetAngle();
  384. aPlotter->ThickArc( wxPoint( center.x, center.y ), -end_angle, -start_angle,
  385. radius, width, plotMode, &gbr_metadata );
  386. }
  387. else
  388. {
  389. aPlotter->ThickSegment( track->GetStart(), track->GetEnd(), width, plotMode,
  390. &gbr_metadata );
  391. }
  392. }
  393. aPlotter->EndBlock( NULL );
  394. // Plot filled ares
  395. aPlotter->StartBlock( NULL );
  396. NETINFO_ITEM nonet( aBoard );
  397. for( ZONE_CONTAINER* zone : aBoard->Zones() )
  398. {
  399. for( PCB_LAYER_ID layer : zone->GetLayerSet().Seq() )
  400. {
  401. if( !aLayerMask[layer] )
  402. continue;
  403. SHAPE_POLY_SET mainArea = zone->GetFilledPolysList( layer );
  404. SHAPE_POLY_SET islands;
  405. for( int i = mainArea.OutlineCount() - 1; i >= 0; i-- )
  406. {
  407. if( zone->IsIsland( layer, i ) )
  408. {
  409. islands.AddOutline( mainArea.CPolygon( i )[0] );
  410. mainArea.DeletePolygon( i );
  411. }
  412. }
  413. itemplotter.PlotFilledAreas( zone, mainArea );
  414. if( !islands.IsEmpty() )
  415. {
  416. ZONE_CONTAINER dummy( *zone );
  417. dummy.SetNet( &nonet );
  418. itemplotter.PlotFilledAreas( &dummy, islands );
  419. }
  420. }
  421. }
  422. aPlotter->EndBlock( NULL );
  423. // Adding drill marks, if required and if the plotter is able to plot them:
  424. if( aPlotOpt.GetDrillMarksType() != PCB_PLOT_PARAMS::NO_DRILL_SHAPE )
  425. itemplotter.PlotDrillMarks();
  426. }
  427. // Seems like we want to plot from back to front?
  428. static const PCB_LAYER_ID plot_seq[] = {
  429. B_Adhes, // 32
  430. F_Adhes,
  431. B_Paste,
  432. F_Paste,
  433. B_SilkS,
  434. B_Mask,
  435. F_Mask,
  436. Dwgs_User,
  437. Cmts_User,
  438. Eco1_User,
  439. Eco2_User,
  440. Edge_Cuts,
  441. Margin,
  442. F_CrtYd, // CrtYd & Body are footprint only
  443. B_CrtYd,
  444. F_Fab,
  445. B_Fab,
  446. B_Cu,
  447. In30_Cu,
  448. In29_Cu,
  449. In28_Cu,
  450. In27_Cu,
  451. In26_Cu,
  452. In25_Cu,
  453. In24_Cu,
  454. In23_Cu,
  455. In22_Cu,
  456. In21_Cu,
  457. In20_Cu,
  458. In19_Cu,
  459. In18_Cu,
  460. In17_Cu,
  461. In16_Cu,
  462. In15_Cu,
  463. In14_Cu,
  464. In13_Cu,
  465. In12_Cu,
  466. In11_Cu,
  467. In10_Cu,
  468. In9_Cu,
  469. In8_Cu,
  470. In7_Cu,
  471. In6_Cu,
  472. In5_Cu,
  473. In4_Cu,
  474. In3_Cu,
  475. In2_Cu,
  476. In1_Cu,
  477. F_Cu,
  478. F_SilkS,
  479. };
  480. /*
  481. * Plot outlines of copper, for copper layer
  482. */
  483. void PlotLayerOutlines( BOARD* aBoard, PLOTTER* aPlotter, LSET aLayerMask,
  484. const PCB_PLOT_PARAMS& aPlotOpt )
  485. {
  486. BRDITEMS_PLOTTER itemplotter( aPlotter, aBoard, aPlotOpt );
  487. itemplotter.SetLayerSet( aLayerMask );
  488. SHAPE_POLY_SET outlines;
  489. for( LSEQ seq = aLayerMask.Seq( plot_seq, arrayDim( plot_seq ) ); seq; ++seq )
  490. {
  491. PCB_LAYER_ID layer = *seq;
  492. outlines.RemoveAllContours();
  493. aBoard->ConvertBrdLayerToPolygonalContours( layer, outlines );
  494. outlines.Simplify( SHAPE_POLY_SET::PM_FAST );
  495. // Plot outlines
  496. std::vector<wxPoint> cornerList;
  497. // Now we have one or more basic polygons: plot each polygon
  498. for( int ii = 0; ii < outlines.OutlineCount(); ii++ )
  499. {
  500. for(int kk = 0; kk <= outlines.HoleCount (ii); kk++ )
  501. {
  502. cornerList.clear();
  503. const SHAPE_LINE_CHAIN& path = (kk == 0) ? outlines.COutline( ii ) : outlines.CHole( ii, kk - 1 );
  504. for( int jj = 0; jj < path.PointCount(); jj++ )
  505. cornerList.emplace_back( (wxPoint) path.CPoint( jj ) );
  506. // Ensure the polygon is closed
  507. if( cornerList[0] != cornerList[cornerList.size() - 1] )
  508. cornerList.push_back( cornerList[0] );
  509. aPlotter->PlotPoly( cornerList, FILL_TYPE::NO_FILL );
  510. }
  511. }
  512. // Plot pad holes
  513. if( aPlotOpt.GetDrillMarksType() != PCB_PLOT_PARAMS::NO_DRILL_SHAPE )
  514. {
  515. int smallDrill = (aPlotOpt.GetDrillMarksType() == PCB_PLOT_PARAMS::SMALL_DRILL_SHAPE)
  516. ? SMALL_DRILL : INT_MAX;
  517. for( MODULE* module : aBoard->Modules() )
  518. {
  519. for( D_PAD* pad : module->Pads() )
  520. {
  521. wxSize hole = pad->GetDrillSize();
  522. if( hole.x == 0 || hole.y == 0 )
  523. continue;
  524. if( hole.x == hole.y )
  525. {
  526. hole.x = std::min( smallDrill, hole.x );
  527. aPlotter->Circle( pad->GetPosition(), hole.x, FILL_TYPE::NO_FILL );
  528. }
  529. else
  530. {
  531. // Note: small drill marks have no significance when applied to slots
  532. const SHAPE_SEGMENT* seg = pad->GetEffectiveHoleShape();
  533. aPlotter->ThickSegment( (wxPoint) seg->GetSeg().A,
  534. (wxPoint) seg->GetSeg().B,
  535. seg->GetWidth(), SKETCH, NULL );
  536. }
  537. }
  538. }
  539. }
  540. // Plot vias holes
  541. for( TRACK* track : aBoard->Tracks() )
  542. {
  543. const VIA* via = dyn_cast<const VIA*>( track );
  544. if( via && via->IsOnLayer( layer ) ) // via holes can be not through holes
  545. {
  546. aPlotter->Circle( via->GetPosition(), via->GetDrillValue(), FILL_TYPE::NO_FILL );
  547. }
  548. }
  549. }
  550. }
  551. /* Plot a solder mask layer.
  552. * Solder mask layers have a minimum thickness value and cannot be drawn like standard layers,
  553. * unless the minimum thickness is 0.
  554. * Currently the algo is:
  555. * 1 - build all pad shapes as polygons with a size inflated by
  556. * mask clearance + (min width solder mask /2)
  557. * 2 - Merge shapes
  558. * 3 - deflate result by (min width solder mask /2)
  559. * 4 - ORing result by all pad shapes as polygons with a size inflated by
  560. * mask clearance only (because deflate sometimes creates shape artifacts)
  561. * 5 - draw result as polygons
  562. *
  563. * We have 2 algos:
  564. * the initial algo, that create polygons for every shape, inflate and deflate polygons
  565. * with Min Thickness/2, and merges the result.
  566. * Drawback: pads attributes are lost (annoying in Gerber)
  567. * the new algo:
  568. * create initial polygons for every shape (pad or polygon),
  569. * inflate and deflate polygons
  570. * with Min Thickness/2, and merges the result (like initial algo)
  571. * remove all initial polygons.
  572. * The remaining polygons are areas with thickness < min thickness
  573. * plot all initial shapes by flashing (or using regions) for pad and polygons
  574. * (shapes will be better) and remaining polygons to
  575. * remove areas with thickness < min thickness from final mask
  576. *
  577. * TODO: remove old code after more testing.
  578. */
  579. #define NEW_ALGO 1
  580. void PlotSolderMaskLayer( BOARD *aBoard, PLOTTER* aPlotter, LSET aLayerMask,
  581. const PCB_PLOT_PARAMS& aPlotOpt, int aMinThickness )
  582. {
  583. int maxError = aBoard->GetDesignSettings().m_MaxError;
  584. PCB_LAYER_ID layer = aLayerMask[B_Mask] ? B_Mask : F_Mask;
  585. SHAPE_POLY_SET buffer;
  586. SHAPE_POLY_SET* boardOutline = nullptr;
  587. if( aBoard->GetBoardPolygonOutlines( buffer ) )
  588. boardOutline = &buffer;
  589. // We remove 1nm as we expand both sides of the shapes, so allowing for
  590. // a strictly greater than or equal comparison in the shape separation (boolean add)
  591. // means that we will end up with separate shapes that then are shrunk
  592. int inflate = aMinThickness/2 - 1;
  593. BRDITEMS_PLOTTER itemplotter( aPlotter, aBoard, aPlotOpt );
  594. itemplotter.SetLayerSet( aLayerMask );
  595. // Plot edge layer and graphic items.
  596. // They do not have a solder Mask margin, because they are graphic items
  597. // on this layer (like logos), not actually areas around pads.
  598. itemplotter.PlotBoardGraphicItems();
  599. for( MODULE* module : aBoard->Modules() )
  600. {
  601. for( BOARD_ITEM* item : module->GraphicalItems() )
  602. {
  603. itemplotter.PlotFootprintTextItems( module );
  604. if( item->Type() == PCB_FP_SHAPE_T && item->GetLayer() == layer )
  605. itemplotter.PlotFootprintGraphicItem( (FP_SHAPE*) item );
  606. }
  607. }
  608. // Build polygons for each pad shape. The size of the shape on solder mask should be size
  609. // of pad + clearance around the pad, where clearance = solder mask clearance + extra margin.
  610. // Extra margin is half the min width for solder mask, which is used to merge too-close shapes
  611. // (distance < aMinThickness), and will be removed when creating the actual shapes.
  612. // Will contain shapes inflated by inflate value that will be merged and deflated by
  613. // inflate value to build final polygons
  614. // After calculations the remaining polygons are polygons to plot
  615. SHAPE_POLY_SET areas;
  616. // Will contain exact shapes of all items on solder mask
  617. SHAPE_POLY_SET initialPolys;
  618. #if NEW_ALGO
  619. // Generate polygons with arcs inside the shape or exact shape
  620. // to minimize shape changes created by arc to segment size correction.
  621. DISABLE_ARC_RADIUS_CORRECTION disabler;
  622. #endif
  623. {
  624. // Plot pads
  625. for( MODULE* module : aBoard->Modules() )
  626. {
  627. // add shapes with their exact mask layer size in initialPolys
  628. module->TransformPadsShapesWithClearanceToPolygon( initialPolys, layer, 0, maxError,
  629. ERROR_OUTSIDE );
  630. // add shapes inflated by aMinThickness/2 in areas
  631. module->TransformPadsShapesWithClearanceToPolygon( areas, layer, inflate, maxError,
  632. ERROR_OUTSIDE );
  633. }
  634. // Plot vias on solder masks, if aPlotOpt.GetPlotViaOnMaskLayer() is true,
  635. if( aPlotOpt.GetPlotViaOnMaskLayer() )
  636. {
  637. // The current layer is a solder mask, use the global mask clearance for vias
  638. int via_clearance = aBoard->GetDesignSettings().m_SolderMaskMargin;
  639. int via_margin = via_clearance + inflate;
  640. for( TRACK* track : aBoard->Tracks() )
  641. {
  642. const VIA* via = dyn_cast<const VIA*>( track );
  643. if( !via )
  644. continue;
  645. // vias are plotted only if they are on the corresponding external copper layer
  646. LSET via_set = via->GetLayerSet();
  647. if( via_set[B_Cu] )
  648. via_set.set( B_Mask );
  649. if( via_set[F_Cu] )
  650. via_set.set( F_Mask );
  651. if( !( via_set & aLayerMask ).any() )
  652. continue;
  653. // add shapes with their exact mask layer size in initialPolys
  654. via->TransformShapeWithClearanceToPolygon( initialPolys, layer, via_clearance,
  655. maxError, ERROR_OUTSIDE );
  656. // add shapes inflated by aMinThickness/2 in areas
  657. via->TransformShapeWithClearanceToPolygon( areas, layer, via_margin, maxError,
  658. ERROR_OUTSIDE );
  659. }
  660. }
  661. // Add filled zone areas.
  662. #if 0 // Set to 1 if a solder mask margin must be applied to zones on solder mask
  663. int zone_margin = aBoard->GetDesignSettings().m_SolderMaskMargin;
  664. #else
  665. int zone_margin = 0;
  666. #endif
  667. for( ZONE_CONTAINER* zone : aBoard->Zones() )
  668. {
  669. if( zone->GetLayer() != layer )
  670. continue;
  671. // add shapes inflated by aMinThickness/2 in areas
  672. zone->TransformSmoothedOutlineToPolygon( areas, inflate + zone_margin, boardOutline );
  673. // add shapes with their exact mask layer size in initialPolys
  674. zone->TransformSmoothedOutlineToPolygon( initialPolys, zone_margin, boardOutline );
  675. }
  676. int numSegs = GetArcToSegmentCount( inflate, maxError, 360.0 );
  677. // Merge all polygons: After deflating, not merged (not overlapping) polygons
  678. // will have the initial shape (with perhaps small changes due to deflating transform)
  679. areas.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
  680. areas.Deflate( inflate, numSegs );
  681. }
  682. #if !NEW_ALGO
  683. // To avoid a lot of code, use a ZONE_CONTAINER to handle and plot polygons, because our
  684. // polygons look exactly like filled areas in zones.
  685. // Note, also this code is not optimized: it creates a lot of copy/duplicate data.
  686. // However it is not complex, and fast enough for plot purposes (copy/convert data is only a
  687. // very small calculation time for these calculations).
  688. ZONE_CONTAINER zone( aBoard );
  689. zone.SetMinThickness( 0 ); // trace polygons only
  690. zone.SetLayer( layer );
  691. // Combine the current areas to initial areas. This is mandatory because inflate/deflate
  692. // transform is not perfect, and we want the initial areas perfectly kept
  693. areas.BooleanAdd( initialPolys, SHAPE_POLY_SET::PM_FAST );
  694. areas.Fracture( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
  695. itemplotter.PlotFilledAreas( &zone, areas );
  696. #else
  697. // Remove initial shapes: each shape will be added later, as flashed item or region
  698. // with a suitable attribute.
  699. // Do not merge pads is mandatory in Gerber files: They must be identified as pads
  700. // we deflate areas in polygons, to avoid after subtracting initial shapes
  701. // having small artifacts due to approximations during polygon transforms
  702. areas.BooleanSubtract( initialPolys, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
  703. // Slightly inflate polygons to avoid any gap between them and other shapes,
  704. // These gaps are created by arc to segments approximations
  705. areas.Inflate( Millimeter2iu( 0.002 ),6 );
  706. // Now, only polygons with a too small thickness are stored in areas.
  707. areas.Fracture( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
  708. // Plot each initial shape (pads and polygons on mask layer), with suitable attributes:
  709. PlotStandardLayer( aBoard, aPlotter, aLayerMask, aPlotOpt );
  710. // Add shapes corresponding to areas having too small thickness.
  711. std::vector<wxPoint> cornerList;
  712. for( int ii = 0; ii < areas.OutlineCount(); ii++ )
  713. {
  714. cornerList.clear();
  715. const SHAPE_LINE_CHAIN& path = areas.COutline( ii );
  716. // polygon area in mm^2 :
  717. double curr_area = path.Area() / ( IU_PER_MM * IU_PER_MM );
  718. // Skip very small polygons: they are certainly artifacts created by
  719. // arc approximations and polygon transforms
  720. // (inflate/deflate transforms)
  721. constexpr double poly_min_area_mm2 = 0.01; // 0.01 mm^2 gives a good filtering
  722. if( curr_area < poly_min_area_mm2 )
  723. continue;
  724. for( int jj = 0; jj < path.PointCount(); jj++ )
  725. cornerList.emplace_back( (wxPoint) path.CPoint( jj ) );
  726. // Ensure the polygon is closed
  727. if( cornerList[0] != cornerList[cornerList.size() - 1] )
  728. cornerList.push_back( cornerList[0] );
  729. aPlotter->PlotPoly( cornerList, FILL_TYPE::FILLED_SHAPE );
  730. }
  731. #endif
  732. }
  733. /**
  734. * Set up most plot options for plotting a board (especially the viewport)
  735. * Important thing:
  736. * page size is the 'drawing' page size,
  737. * paper size is the physical page size
  738. */
  739. static void initializePlotter( PLOTTER *aPlotter, BOARD * aBoard,
  740. PCB_PLOT_PARAMS *aPlotOpts )
  741. {
  742. PAGE_INFO pageA4( wxT( "A4" ) );
  743. const PAGE_INFO& pageInfo = aBoard->GetPageSettings();
  744. const PAGE_INFO* sheet_info;
  745. double paperscale; // Page-to-paper ratio
  746. wxSize paperSizeIU;
  747. wxSize pageSizeIU( pageInfo.GetSizeIU() );
  748. bool autocenter = false;
  749. // Special options: to fit the sheet to an A4 sheet replace the paper size. However there
  750. // is a difference between the autoscale and the a4paper option:
  751. // - Autoscale fits the board to the paper size
  752. // - A4paper fits the original paper size to an A4 sheet
  753. // - Both of them fit the board to an A4 sheet
  754. if( aPlotOpts->GetA4Output() )
  755. {
  756. sheet_info = &pageA4;
  757. paperSizeIU = pageA4.GetSizeIU();
  758. paperscale = (double) paperSizeIU.x / pageSizeIU.x;
  759. autocenter = true;
  760. }
  761. else
  762. {
  763. sheet_info = &pageInfo;
  764. paperSizeIU = pageSizeIU;
  765. paperscale = 1;
  766. // Need autocentering only if scale is not 1:1
  767. autocenter = (aPlotOpts->GetScale() != 1.0);
  768. }
  769. EDA_RECT bbox = aBoard->ComputeBoundingBox();
  770. wxPoint boardCenter = bbox.Centre();
  771. wxSize boardSize = bbox.GetSize();
  772. double compound_scale;
  773. // Fit to 80% of the page if asked; it could be that the board is empty, in this case
  774. // regress to 1:1 scale
  775. if( aPlotOpts->GetAutoScale() && boardSize.x > 0 && boardSize.y > 0 )
  776. {
  777. double xscale = (paperSizeIU.x * 0.8) / boardSize.x;
  778. double yscale = (paperSizeIU.y * 0.8) / boardSize.y;
  779. compound_scale = std::min( xscale, yscale ) * paperscale;
  780. }
  781. else
  782. compound_scale = aPlotOpts->GetScale() * paperscale;
  783. // For the plot offset we have to keep in mind the auxiliary origin too: if autoscaling is
  784. // off we check that plot option (i.e. autoscaling overrides auxiliary origin)
  785. wxPoint offset( 0, 0);
  786. if( autocenter )
  787. {
  788. offset.x = KiROUND( boardCenter.x - ( paperSizeIU.x / 2.0 ) / compound_scale );
  789. offset.y = KiROUND( boardCenter.y - ( paperSizeIU.y / 2.0 ) / compound_scale );
  790. }
  791. else
  792. {
  793. if( aPlotOpts->GetUseAuxOrigin() )
  794. offset = aBoard->GetDesignSettings().m_AuxOrigin;
  795. }
  796. aPlotter->SetPageSettings( *sheet_info );
  797. aPlotter->SetViewport( offset, IU_PER_MILS/10, compound_scale, aPlotOpts->GetMirror() );
  798. // Has meaning only for gerber plotter. Must be called only after SetViewport
  799. aPlotter->SetGerberCoordinatesFormat( aPlotOpts->GetGerberPrecision() );
  800. // Has meaning only for SVG plotter. Must be called only after SetViewport
  801. aPlotter->SetSvgCoordinatesFormat( aPlotOpts->GetSvgPrecision(), aPlotOpts->GetSvgUseInch() );
  802. aPlotter->SetCreator( wxT( "PCBNEW" ) );
  803. aPlotter->SetColorMode( false ); // default is plot in Black and White.
  804. aPlotter->SetTextMode( aPlotOpts->GetTextMode() );
  805. }
  806. /**
  807. * Prefill in black an area a little bigger than the board to prepare for the negative plot
  808. */
  809. static void FillNegativeKnockout( PLOTTER *aPlotter, const EDA_RECT &aBbbox )
  810. {
  811. const int margin = 5 * IU_PER_MM; // Add a 5 mm margin around the board
  812. aPlotter->SetNegative( true );
  813. aPlotter->SetColor( WHITE ); // Which will be plotted as black
  814. EDA_RECT area = aBbbox;
  815. area.Inflate( margin );
  816. aPlotter->Rect( area.GetOrigin(), area.GetEnd(), FILL_TYPE::FILLED_SHAPE );
  817. aPlotter->SetColor( BLACK );
  818. }
  819. /**
  820. * Calculate the effective size of HPGL pens and set them in the plotter object
  821. */
  822. static void ConfigureHPGLPenSizes( HPGL_PLOTTER *aPlotter, PCB_PLOT_PARAMS *aPlotOpts )
  823. {
  824. // Compute penDiam (the value is given in mils) in pcb units, with plot scale (if Scale is 2,
  825. // penDiam value is always m_HPGLPenDiam so apparent penDiam is actually penDiam / Scale
  826. int penDiam = KiROUND( aPlotOpts->GetHPGLPenDiameter() * IU_PER_MILS / aPlotOpts->GetScale() );
  827. // Set HPGL-specific options and start
  828. aPlotter->SetPenSpeed( aPlotOpts->GetHPGLPenSpeed() );
  829. aPlotter->SetPenNumber( aPlotOpts->GetHPGLPenNum() );
  830. aPlotter->SetPenDiameter( penDiam );
  831. }
  832. /**
  833. * Open a new plotfile using the options (and especially the format) specified in the options
  834. * and prepare the page for plotting.
  835. * Return the plotter object if OK, NULL if the file is not created (or has a problem)
  836. */
  837. PLOTTER* StartPlotBoard( BOARD *aBoard, PCB_PLOT_PARAMS *aPlotOpts, int aLayer,
  838. const wxString& aFullFileName, const wxString& aSheetDesc )
  839. {
  840. // Create the plotter driver and set the few plotter specific options
  841. PLOTTER* plotter = NULL;
  842. switch( aPlotOpts->GetFormat() )
  843. {
  844. case PLOT_FORMAT::DXF:
  845. DXF_PLOTTER* DXF_plotter;
  846. DXF_plotter = new DXF_PLOTTER();
  847. DXF_plotter->SetUnits( aPlotOpts->GetDXFPlotUnits() );
  848. plotter = DXF_plotter;
  849. break;
  850. case PLOT_FORMAT::POST:
  851. PS_PLOTTER* PS_plotter;
  852. PS_plotter = new PS_PLOTTER();
  853. PS_plotter->SetScaleAdjust( aPlotOpts->GetFineScaleAdjustX(),
  854. aPlotOpts->GetFineScaleAdjustY() );
  855. plotter = PS_plotter;
  856. break;
  857. case PLOT_FORMAT::PDF:
  858. plotter = new PDF_PLOTTER();
  859. break;
  860. case PLOT_FORMAT::HPGL:
  861. HPGL_PLOTTER* HPGL_plotter;
  862. HPGL_plotter = new HPGL_PLOTTER();
  863. // HPGL options are a little more convoluted to compute, so they get their own function
  864. ConfigureHPGLPenSizes( HPGL_plotter, aPlotOpts );
  865. plotter = HPGL_plotter;
  866. break;
  867. case PLOT_FORMAT::GERBER:
  868. plotter = new GERBER_PLOTTER();
  869. break;
  870. case PLOT_FORMAT::SVG:
  871. plotter = new SVG_PLOTTER();
  872. break;
  873. default:
  874. wxASSERT( false );
  875. return NULL;
  876. }
  877. KIGFX::PCB_RENDER_SETTINGS* renderSettings = new KIGFX::PCB_RENDER_SETTINGS();
  878. renderSettings->LoadColors( aPlotOpts->ColorSettings() );
  879. renderSettings->SetDefaultPenWidth( Millimeter2iu( 0.0212 ) ); // Hairline at 1200dpi
  880. plotter->SetRenderSettings( renderSettings );
  881. // Compute the viewport and set the other options
  882. // page layout is not mirrored, so temporarily change mirror option for the page layout
  883. PCB_PLOT_PARAMS plotOpts = *aPlotOpts;
  884. if( plotOpts.GetPlotFrameRef() && plotOpts.GetMirror() )
  885. plotOpts.SetMirror( false );
  886. initializePlotter( plotter, aBoard, &plotOpts );
  887. if( plotter->OpenFile( aFullFileName ) )
  888. {
  889. plotter->ClearHeaderLinesList();
  890. // For the Gerber "file function" attribute, set the layer number
  891. if( plotter->GetPlotterType() == PLOT_FORMAT::GERBER )
  892. {
  893. bool useX2mode = plotOpts.GetUseGerberX2format();
  894. GERBER_PLOTTER* gbrplotter = static_cast <GERBER_PLOTTER*> ( plotter );
  895. gbrplotter->DisableApertMacros( plotOpts.GetDisableGerberMacros() );
  896. gbrplotter->UseX2format( useX2mode );
  897. gbrplotter->UseX2NetAttributes( plotOpts.GetIncludeGerberNetlistInfo() );
  898. // Attributes can be added using X2 format or as comment (X1 format)
  899. AddGerberX2Attribute( plotter, aBoard, aLayer, not useX2mode );
  900. }
  901. plotter->StartPlot();
  902. // Plot the frame reference if requested
  903. if( aPlotOpts->GetPlotFrameRef() )
  904. {
  905. PlotWorkSheet( plotter, aBoard->GetProject(), aBoard->GetTitleBlock(),
  906. aBoard->GetPageSettings(), "1", 1, aSheetDesc, aBoard->GetFileName() );
  907. if( aPlotOpts->GetMirror() )
  908. initializePlotter( plotter, aBoard, aPlotOpts );
  909. }
  910. // When plotting a negative board: draw a black rectangle (background for plot board
  911. // in white) and switch the current color to WHITE; note the color inversion is actually
  912. // done in the driver (if supported)
  913. if( aPlotOpts->GetNegative() )
  914. {
  915. EDA_RECT bbox = aBoard->ComputeBoundingBox();
  916. FillNegativeKnockout( plotter, bbox );
  917. }
  918. return plotter;
  919. }
  920. delete plotter->RenderSettings();
  921. delete plotter;
  922. return NULL;
  923. }